Skip to content

Commit 4f26016

Browse files
Introduce INSTALLLOC_DIRECTORY_CONTENTS to allow installloc to support installing ad-hoc bundle sub-contents (#838)
1 parent bf89c95 commit 4f26016

File tree

5 files changed

+239
-1
lines changed

5 files changed

+239
-1
lines changed

Sources/SWBCore/Settings/BuiltinMacros.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ public final class BuiltinMacros {
780780
public static let INPUT_FILE_SUFFIX = BuiltinMacros.declareStringMacro("INPUT_FILE_SUFFIX")
781781
public static let INSTALLHDRS_COPY_PHASE = BuiltinMacros.declareBooleanMacro("INSTALLHDRS_COPY_PHASE")
782782
public static let INSTALLHDRS_SCRIPT_PHASE = BuiltinMacros.declareBooleanMacro("INSTALLHDRS_SCRIPT_PHASE")
783+
public static let INSTALLLOC_DIRECTORY_CONTENTS = BuiltinMacros.declareBooleanMacro("INSTALLLOC_DIRECTORY_CONTENTS")
783784
public static let INSTALLLOC_LANGUAGE = BuiltinMacros.declareStringListMacro("INSTALLLOC_LANGUAGE")
784785
public static let INSTALLLOC_SCRIPT_PHASE = BuiltinMacros.declareBooleanMacro("INSTALLLOC_SCRIPT_PHASE")
785786
public static let INSTALL_DIR = BuiltinMacros.declarePathMacro("INSTALL_DIR")
@@ -1877,6 +1878,7 @@ public final class BuiltinMacros {
18771878
INSTALLED_PRODUCT_ASIDES,
18781879
INSTALLHDRS_COPY_PHASE,
18791880
INSTALLHDRS_SCRIPT_PHASE,
1881+
INSTALLLOC_DIRECTORY_CONTENTS,
18801882
INSTALLLOC_LANGUAGE,
18811883
INSTALLLOC_SCRIPT_PHASE,
18821884
INSTALL_DIR,

Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/CopyFilesTaskProducer.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ class CopyFilesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, FilesBasedBui
226226
return
227227
}
228228
}
229+
} else if scope.evaluate(BuiltinMacros.INSTALLLOC_DIRECTORY_CONTENTS), !ftb.isValidLocalizedContent(scope), context.workspaceContext.fs.isDirectory(ftb.absolutePath) {
230+
// Treat any package or directory the same as a copied bundle and copy over the relevant lproj directories.
231+
await addTasksForEmbeddedLocalizedBundle(ftb, buildFilesContext, scope, &tasks)
232+
return
229233
}
230234
guard ftb.isValidLocalizedContent(scope) || targetBundleProduct else { return }
231235
}

Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/ResourcesTaskProducer.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ final class ResourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, FilesBa
163163
await addTasksForEmbeddedLocalizedBundle(ftb, buildFilesContext, scope, &tasks)
164164
return
165165
}
166+
} else if scope.evaluate(BuiltinMacros.INSTALLLOC_DIRECTORY_CONTENTS), !ftb.isValidLocalizedContent(scope), context.workspaceContext.fs.isDirectory(ftb.absolutePath) {
167+
// Treat any package or directory the same as a copied bundle and copy over the relevant lproj directories.
168+
await addTasksForEmbeddedLocalizedBundle(ftb, buildFilesContext, scope, &tasks)
169+
return
166170
}
167171
guard ftb.isValidLocalizedContent(scope) || targetBundleProduct else { return }
168172
}

Sources/SWBTestSupport/TestWorkspaces.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ package final class TestFile: TestInternalStructureItem, CustomStringConvertible
315315
return "folder.documentationcatalog"
316316
case ".tightbeam":
317317
return "sourcecode.tightbeam"
318+
case ".myPackage":
319+
return "com.apple.package"
320+
case "":
321+
return "public.directory"
318322
case let ext where ext.hasPrefix(".fake-"):
319323
// If this is a fake extension, just return "file".
320324
return "file"

Tests/SWBTaskConstructionTests/InstallLocTaskConstructionTests.swift

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ fileprivate struct InstallLocTaskConstructionTests: CoreBasedTests {
630630
results.checkNoDiagnostics()
631631
}
632632

633-
// INSTALLLOC_LANGUAGE set to "ja"
633+
// INSTALLLOC_LANGUAGE set to "zh_TW" and "ja"
634634
let specificLangs = ["zh_TW", "ja"]
635635
await tester.checkBuild(BuildParameters(action: .installLoc, configuration: "Debug", overrides: ["INSTALLLOC_LANGUAGE": specificLangs.joined(separator: " ")]), runDestination: .iOS, fs: fs) { results in
636636
// Ignore all Gate, build directory, MkDir, and SymLink tasks.
@@ -653,6 +653,230 @@ fileprivate struct InstallLocTaskConstructionTests: CoreBasedTests {
653653
}
654654
}
655655

656+
/// Test an App that has a directory or package (not an explicit bundle) in resources phase
657+
@Test(.requireSDKs(.macOS))
658+
func embeddedDirectoriesInResources() async throws {
659+
try await withTemporaryDirectory { tmpDir in
660+
let srcRoot = tmpDir.join("srcroot")
661+
662+
let testProject = TestProject(
663+
"aProject",
664+
sourceRoot: srcRoot,
665+
groupTree: TestGroup(
666+
"SomeFiles", path: "Sources",
667+
children: [
668+
TestFile("MyFolder"),
669+
TestFile("MyPackage.myPackage"),
670+
]),
671+
buildConfigurations: [
672+
TestBuildConfiguration(
673+
"Debug",
674+
buildSettings: [
675+
"PRODUCT_NAME": "$(TARGET_NAME)",
676+
"INSTALLLOC_DIRECTORY_CONTENTS": "YES"
677+
]),
678+
],
679+
targets: [
680+
TestStandardTarget(
681+
"App",
682+
type: .application,
683+
buildPhases: [
684+
TestResourcesBuildPhase([
685+
"MyFolder",
686+
"MyPackage.myPackage"
687+
], onlyForDeployment: false)
688+
]
689+
)
690+
])
691+
let tester = try await TaskConstructionTester(getCore(), testProject)
692+
let fs = PseudoFS()
693+
var languageComponentPathGroupings: [(lang: String, component: String, path: Path)] = []
694+
for pathComponent in ["MyFolder", "MyPackage.myPackage"] {
695+
let bundlePath = srcRoot.join("Sources/\(pathComponent)", preserveRoot: true, normalize: true)
696+
try fs.createDirectory(bundlePath, recursive: true)
697+
try fs.write(bundlePath.join("Info.plist"), contents: "LocTest")
698+
699+
for lang in ["en", "ja", "zh_TW"] {
700+
let path = bundlePath.join("\(lang).lproj", preserveRoot: true, normalize: true)
701+
try fs.createDirectory(path, recursive: false)
702+
try fs.write(path.join("Localizable.strings"), contents: "LocTest")
703+
languageComponentPathGroupings += [(lang, pathComponent, path)]
704+
}
705+
}
706+
707+
await tester.checkBuild(BuildParameters(action: .installLoc, configuration: "Debug"), runDestination: .iOS, fs: fs) { results in
708+
// Ignore all Gate, build directory, SymLink, and MkDir tasks.
709+
results.checkTasks(.matchRuleType("Gate")) { _ in }
710+
results.checkTasks(.matchRuleType("CreateBuildDirectory")) { _ in }
711+
results.checkTasks(.matchRuleType("SymLink")) { _ in }
712+
results.checkTasks(.matchRuleType("MkDir")) { _ in }
713+
results.checkTasks(.matchRuleType("WriteAuxiliaryFile")) { _ in }
714+
results.checkTasks(.matchRuleType("ProcessInfoPlistFile")) { _ in }
715+
716+
results.checkTarget("App") { target in
717+
for (language, component, path) in languageComponentPathGroupings {
718+
results.checkTask(.matchTarget(target), .matchRule(["CpResource", "/tmp/aProject.dst/Applications/App.app/Contents/Resources/\(component)/\(language).lproj", path.str])) { _ in }
719+
}
720+
}
721+
results.checkNoTask()
722+
results.checkNoDiagnostics()
723+
}
724+
725+
// INSTALLLOC_LANGUAGE set to "ja"
726+
await tester.checkBuild(BuildParameters(action: .installLoc, configuration: "Debug", overrides: ["INSTALLLOC_LANGUAGE": "ja"]), runDestination: .iOS, fs: fs) { results in
727+
// Ignore all Gate, build directory, MkDir, and SymLink tasks.
728+
results.checkTasks(.matchRuleType("Gate")) { _ in }
729+
results.checkTasks(.matchRuleType("CreateBuildDirectory")) { _ in }
730+
results.checkTasks(.matchRuleType("MkDir")) { _ in }
731+
results.checkTasks(.matchRuleType("SymLink")) { _ in }
732+
results.checkTasks(.matchRuleType("LinkStoryboards")) { _ in }
733+
results.checkTasks(.matchRuleType("WriteAuxiliaryFile")) { _ in }
734+
735+
results.checkTarget("App") { target in
736+
for (language, component, path) in languageComponentPathGroupings where language == "ja" {
737+
results.checkTask(.matchTarget(target), .matchRule(["CpResource", "/tmp/aProject.dst/Applications/App.app/Contents/Resources/\(component)/\(language).lproj", path.str])) { _ in }
738+
}
739+
}
740+
741+
results.checkNoTask()
742+
results.checkNoDiagnostics()
743+
}
744+
745+
// INSTALLLOC_LANGUAGE set to "zh_TW" and "ja"
746+
let specificLangs = ["zh_TW", "ja"]
747+
await tester.checkBuild(BuildParameters(action: .installLoc, configuration: "Debug", overrides: ["INSTALLLOC_LANGUAGE": specificLangs.joined(separator: " ")]), runDestination: .iOS, fs: fs) { results in
748+
// Ignore all Gate, build directory, MkDir, and SymLink tasks.
749+
results.checkTasks(.matchRuleType("Gate")) { _ in }
750+
results.checkTasks(.matchRuleType("CreateBuildDirectory")) { _ in }
751+
results.checkTasks(.matchRuleType("MkDir")) { _ in }
752+
results.checkTasks(.matchRuleType("SymLink")) { _ in }
753+
results.checkTasks(.matchRuleType("LinkStoryboards")) { _ in }
754+
results.checkTasks(.matchRuleType("WriteAuxiliaryFile")) { _ in }
755+
756+
results.checkTarget("App") { target in
757+
for (language, component, path) in languageComponentPathGroupings where specificLangs.contains(language) {
758+
results.checkTask(.matchTarget(target), .matchRule(["CpResource", "/tmp/aProject.dst/Applications/App.app/Contents/Resources/\(component)/\(language).lproj", path.str])) { _ in }
759+
}
760+
}
761+
762+
results.checkNoTask()
763+
results.checkNoDiagnostics()
764+
}
765+
}
766+
}
767+
768+
/// Test an App that has a directory or package (not an explicit bundle) in copy files phase
769+
@Test(.requireSDKs(.macOS))
770+
func embeddedDirectoriesInCopyFiles() async throws {
771+
try await withTemporaryDirectory { tmpDir in
772+
let srcRoot = tmpDir.join("srcroot")
773+
774+
let testProject = TestProject(
775+
"aProject",
776+
sourceRoot: srcRoot,
777+
groupTree: TestGroup(
778+
"SomeFiles", path: "Sources",
779+
children: [
780+
TestFile("MyFolder"),
781+
TestFile("MyPackage.myPackage"),
782+
]),
783+
buildConfigurations: [
784+
TestBuildConfiguration(
785+
"Debug",
786+
buildSettings: [
787+
"PRODUCT_NAME": "$(TARGET_NAME)",
788+
"INSTALLLOC_DIRECTORY_CONTENTS": "YES"
789+
]),
790+
],
791+
targets: [
792+
TestStandardTarget(
793+
"App",
794+
type: .application,
795+
buildPhases: [
796+
TestCopyFilesBuildPhase([
797+
"MyFolder",
798+
"MyPackage.myPackage"
799+
], destinationSubfolder: .absolute, destinationSubpath: "/tmp/CustomPath", onlyForDeployment: false)
800+
]
801+
)
802+
])
803+
let tester = try await TaskConstructionTester(getCore(), testProject)
804+
let fs = PseudoFS()
805+
var languageComponentPathGroupings: [(lang: String, component: String, path: Path)] = []
806+
for pathComponent in ["MyFolder", "MyPackage.myPackage"] {
807+
let bundlePath = srcRoot.join("Sources/\(pathComponent)", preserveRoot: true, normalize: true)
808+
try fs.createDirectory(bundlePath, recursive: true)
809+
try fs.write(bundlePath.join("Info.plist"), contents: "LocTest")
810+
811+
for lang in ["en", "ja", "zh_TW"] {
812+
let path = bundlePath.join("\(lang).lproj", preserveRoot: true, normalize: true)
813+
try fs.createDirectory(path, recursive: false)
814+
try fs.write(path.join("Localizable.strings"), contents: "LocTest")
815+
languageComponentPathGroupings += [(lang, pathComponent, path)]
816+
}
817+
}
818+
819+
await tester.checkBuild(BuildParameters(action: .installLoc, configuration: "Debug"), runDestination: .iOS, fs: fs) { results in
820+
// Ignore all Gate, build directory, SymLink, and MkDir tasks.
821+
results.checkTasks(.matchRuleType("Gate")) { _ in }
822+
results.checkTasks(.matchRuleType("CreateBuildDirectory")) { _ in }
823+
results.checkTasks(.matchRuleType("SymLink")) { _ in }
824+
results.checkTasks(.matchRuleType("MkDir")) { _ in }
825+
results.checkTasks(.matchRuleType("WriteAuxiliaryFile")) { _ in }
826+
results.checkTasks(.matchRuleType("ProcessInfoPlistFile")) { _ in }
827+
828+
results.checkTarget("App") { target in
829+
for (language, component, path) in languageComponentPathGroupings {
830+
results.checkTask(.matchTarget(target), .matchRule(["Copy", "/tmp/aProject.dst/tmp/CustomPath/\(component)/\(language).lproj", path.str])) { _ in }
831+
}
832+
}
833+
results.checkNoTask()
834+
results.checkNoDiagnostics()
835+
}
836+
837+
// INSTALLLOC_LANGUAGE set to "ja"
838+
await tester.checkBuild(BuildParameters(action: .installLoc, configuration: "Debug", overrides: ["INSTALLLOC_LANGUAGE": "ja"]), runDestination: .iOS, fs: fs) { results in
839+
// Ignore all Gate, build directory, MkDir, and SymLink tasks.
840+
results.checkTasks(.matchRuleType("Gate")) { _ in }
841+
results.checkTasks(.matchRuleType("CreateBuildDirectory")) { _ in }
842+
results.checkTasks(.matchRuleType("MkDir")) { _ in }
843+
results.checkTasks(.matchRuleType("SymLink")) { _ in }
844+
results.checkTasks(.matchRuleType("LinkStoryboards")) { _ in }
845+
results.checkTasks(.matchRuleType("WriteAuxiliaryFile")) { _ in }
846+
847+
results.checkTarget("App") { target in
848+
for (language, component, path) in languageComponentPathGroupings where language == "ja" {
849+
results.checkTask(.matchTarget(target), .matchRule(["Copy", "/tmp/aProject.dst/tmp/CustomPath/\(component)/\(language).lproj", path.str])) { _ in }
850+
}
851+
}
852+
853+
results.checkNoTask()
854+
results.checkNoDiagnostics()
855+
}
856+
857+
// INSTALLLOC_LANGUAGE set to "zh_TW" and "ja"
858+
let specificLangs = ["zh_TW", "ja"]
859+
await tester.checkBuild(BuildParameters(action: .installLoc, configuration: "Debug", overrides: ["INSTALLLOC_LANGUAGE": specificLangs.joined(separator: " ")]), runDestination: .iOS, fs: fs) { results in
860+
// Ignore all Gate, build directory, MkDir, and SymLink tasks.
861+
results.checkTasks(.matchRuleType("Gate")) { _ in }
862+
results.checkTasks(.matchRuleType("CreateBuildDirectory")) { _ in }
863+
results.checkTasks(.matchRuleType("MkDir")) { _ in }
864+
results.checkTasks(.matchRuleType("SymLink")) { _ in }
865+
results.checkTasks(.matchRuleType("LinkStoryboards")) { _ in }
866+
results.checkTasks(.matchRuleType("WriteAuxiliaryFile")) { _ in }
867+
868+
results.checkTarget("App") { target in
869+
for (language, component, path) in languageComponentPathGroupings where specificLangs.contains(language) {
870+
results.checkTask(.matchTarget(target), .matchRule(["Copy", "/tmp/aProject.dst/tmp/CustomPath/\(component)/\(language).lproj", path.str])) { _ in }
871+
}
872+
}
873+
874+
results.checkNoTask()
875+
results.checkNoDiagnostics()
876+
}
877+
}
878+
}
879+
656880
/// Test an App that has a bundle (not the product of a target) in copy files phase
657881
@Test(.requireSDKs(.iOS))
658882
func warningForMissingExternalBundles() async throws {

0 commit comments

Comments
 (0)