Add Darwin x86_64+arm64 universal binary support
This is configured from Make by setting up Darwin+Arm64 as a HOST_CROSS target (which is largely true, as binaries can't be executed on X86_64 machines). On the Soong side, it's a bit blurier, as we don't current have any other users that are the same OS but not natively executable (Linux/musl is executable, Windows/x86 is a different OS). Instead of requiring cc_binary/etc to become multi-architecture-aware and using something like common_first/MultiTarget, this defaults all non-multi-architecture-aware modules to be built with both architectures. It then adds a dependency with the DarwinUniversalVariantTag so that supporting modules can get the outputs of the other variant. Cc uses that dependency tag to run lipo on shared libraries and binaries so that the output of the x86_64 variant is actually a fat binary including both architectures. Bug: 203607969 Test: build sdk-repo targets on a Mac Change-Id: Icbddb0a177c0ba19d3e0d11f8cf568e0d1ea3245
This commit is contained in:
@@ -566,6 +566,8 @@ func GetOsSpecificVariantsOfCommonOSVariant(mctx BaseModuleContext) []Module {
|
||||
return variants
|
||||
}
|
||||
|
||||
var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"}
|
||||
|
||||
// archMutator splits a module into a variant for each Target requested by the module. Target selection
|
||||
// for a module is in three levels, OsClass, multilib, and then Target.
|
||||
// OsClass selection is determined by:
|
||||
@@ -652,7 +654,7 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) {
|
||||
prefer32 := os == Windows
|
||||
|
||||
// Determine the multilib selection for this module.
|
||||
multilib, extraMultilib := decodeMultilib(base, os.Class)
|
||||
multilib, extraMultilib := decodeMultilib(base, os)
|
||||
|
||||
// Convert the multilib selection into a list of Targets.
|
||||
targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
|
||||
@@ -702,6 +704,16 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) {
|
||||
m.base().commonProperties.SkipInstall = true
|
||||
}
|
||||
}
|
||||
|
||||
// Create a dependency for Darwin Universal binaries from the primary to secondary
|
||||
// architecture. The module itself will be responsible for calling lipo to merge the outputs.
|
||||
if os == Darwin {
|
||||
if multilib == "darwin_universal" && len(modules) == 2 {
|
||||
mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[1], modules[0])
|
||||
} else if multilib == "darwin_universal_common_first" && len(modules) == 3 {
|
||||
mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[2], modules[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// addTargetProperties annotates a variant with the Target is is being compiled for, the list
|
||||
@@ -717,9 +729,9 @@ func addTargetProperties(m Module, target Target, multiTargets []Target, primary
|
||||
// multilib from the factory's call to InitAndroidArchModule if none was set. For modules that
|
||||
// called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
|
||||
// the actual multilib in extraMultilib.
|
||||
func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib string) {
|
||||
func decodeMultilib(base *ModuleBase, os OsType) (multilib, extraMultilib string) {
|
||||
// First check the "android.compile_multilib" or "host.compile_multilib" properties.
|
||||
switch class {
|
||||
switch os.Class {
|
||||
case Device:
|
||||
multilib = String(base.commonProperties.Target.Android.Compile_multilib)
|
||||
case Host:
|
||||
@@ -737,6 +749,26 @@ func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib st
|
||||
}
|
||||
|
||||
if base.commonProperties.UseTargetVariants {
|
||||
// Darwin has the concept of "universal binaries" which is implemented in Soong by
|
||||
// building both x86_64 and arm64 variants, and having select module types know how to
|
||||
// merge the outputs of their corresponding variants together into a final binary. Most
|
||||
// module types don't need to understand this logic, as we only build a small portion
|
||||
// of the tree for Darwin, and only module types writing macho files need to do the
|
||||
// merging.
|
||||
//
|
||||
// This logic is not enabled for:
|
||||
// "common", as it's not an arch-specific variant
|
||||
// "32", as Darwin never has a 32-bit variant
|
||||
// !UseTargetVariants, as the module has opted into handling the arch-specific logic on
|
||||
// its own.
|
||||
if os == Darwin && multilib != "common" && multilib != "32" {
|
||||
if multilib == "common_first" {
|
||||
multilib = "darwin_universal_common_first"
|
||||
} else {
|
||||
multilib = "darwin_universal"
|
||||
}
|
||||
}
|
||||
|
||||
return multilib, ""
|
||||
} else {
|
||||
// For app modules a single arch variant will be created per OS class which is expected to handle all the
|
||||
@@ -1793,6 +1825,15 @@ func decodeMultilibTargets(multilib string, targets []Target, prefer32 bool) ([]
|
||||
if len(buildTargets) == 0 {
|
||||
buildTargets = filterMultilibTargets(targets, "lib64")
|
||||
}
|
||||
case "darwin_universal":
|
||||
buildTargets = filterMultilibTargets(targets, "lib64")
|
||||
// Reverse the targets so that the first architecture can depend on the second
|
||||
// architecture module in order to merge the outputs.
|
||||
reverseSliceInPlace(buildTargets)
|
||||
case "darwin_universal_common_first":
|
||||
archTargets := filterMultilibTargets(targets, "lib64")
|
||||
reverseSliceInPlace(archTargets)
|
||||
buildTargets = append(getCommonTargets(targets), archTargets...)
|
||||
default:
|
||||
return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`,
|
||||
multilib)
|
||||
|
@@ -2015,6 +2015,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
|
||||
}
|
||||
} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
|
||||
// nothing
|
||||
} else if depTag == android.DarwinUniversalVariantTag {
|
||||
// nothing
|
||||
} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
|
||||
ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
|
||||
}
|
||||
|
@@ -345,6 +345,12 @@ func (binary *binaryDecorator) link(ctx ModuleContext,
|
||||
flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-dynamic-linker")
|
||||
}
|
||||
|
||||
if ctx.Darwin() && deps.DarwinSecondArchOutput.Valid() {
|
||||
fatOutputFile := outputFile
|
||||
outputFile = android.PathForModuleOut(ctx, "pre-fat", fileName)
|
||||
transformDarwinUniversalBinary(ctx, fatOutputFile, outputFile, deps.DarwinSecondArchOutput.Path())
|
||||
}
|
||||
|
||||
builderFlags := flagsToBuilderFlags(flags)
|
||||
stripFlags := flagsToStripFlags(flags)
|
||||
if binary.stripper.NeedsStrip(ctx) {
|
||||
|
@@ -165,6 +165,12 @@ var (
|
||||
}
|
||||
}()
|
||||
|
||||
darwinLipo = pctx.AndroidStaticRule("darwinLipo",
|
||||
blueprint.RuleParams{
|
||||
Command: "${config.MacLipoPath} -create -output $out $in",
|
||||
CommandDeps: []string{"${config.MacLipoPath}"},
|
||||
})
|
||||
|
||||
_ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh")
|
||||
|
||||
// Rule to repack an archive (.a) file with a subset of object files.
|
||||
@@ -1060,6 +1066,15 @@ func transformDarwinStrip(ctx android.ModuleContext, inputFile android.Path,
|
||||
})
|
||||
}
|
||||
|
||||
func transformDarwinUniversalBinary(ctx android.ModuleContext, outputFile android.WritablePath, inputFiles ...android.Path) {
|
||||
ctx.Build(pctx, android.BuildParams{
|
||||
Rule: darwinLipo,
|
||||
Description: "lipo " + outputFile.Base(),
|
||||
Output: outputFile,
|
||||
Inputs: inputFiles,
|
||||
})
|
||||
}
|
||||
|
||||
// Registers build statement to zip one or more coverage files.
|
||||
func transformCoverageFilesToZip(ctx android.ModuleContext,
|
||||
inputs Objects, baseName string) android.OptionalPath {
|
||||
|
9
cc/cc.go
9
cc/cc.go
@@ -167,6 +167,10 @@ type PathDeps struct {
|
||||
|
||||
// Path to the dynamic linker binary
|
||||
DynamicLinker android.OptionalPath
|
||||
|
||||
// For Darwin builds, the path to the second architecture's output that should
|
||||
// be combined with this architectures's output into a FAT MachO file.
|
||||
DarwinSecondArchOutput android.OptionalPath
|
||||
}
|
||||
|
||||
// LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
|
||||
@@ -2578,6 +2582,11 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
|
||||
depName := ctx.OtherModuleName(dep)
|
||||
depTag := ctx.OtherModuleDependencyTag(dep)
|
||||
|
||||
if depTag == android.DarwinUniversalVariantTag {
|
||||
depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
|
||||
return
|
||||
}
|
||||
|
||||
ccDep, ok := dep.(LinkableInterface)
|
||||
if !ok {
|
||||
|
||||
|
@@ -87,6 +87,10 @@ func init() {
|
||||
return getMacTools(ctx).arPath
|
||||
})
|
||||
|
||||
pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string {
|
||||
return getMacTools(ctx).lipoPath
|
||||
})
|
||||
|
||||
pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string {
|
||||
return getMacTools(ctx).stripPath
|
||||
})
|
||||
@@ -118,6 +122,7 @@ type macPlatformTools struct {
|
||||
|
||||
sdkRoot string
|
||||
arPath string
|
||||
lipoPath string
|
||||
stripPath string
|
||||
toolPath string
|
||||
}
|
||||
@@ -157,6 +162,7 @@ func getMacTools(ctx android.PathContext) *macPlatformTools {
|
||||
macTools.sdkRoot = xcrun("--show-sdk-path")
|
||||
|
||||
macTools.arPath = xcrun("--find", "ar")
|
||||
macTools.lipoPath = xcrun("--find", "lipo")
|
||||
macTools.stripPath = xcrun("--find", "strip")
|
||||
macTools.toolPath = filepath.Dir(xcrun("--find", "ld"))
|
||||
})
|
||||
|
@@ -1429,6 +1429,12 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext,
|
||||
|
||||
builderFlags := flagsToBuilderFlags(flags)
|
||||
|
||||
if ctx.Darwin() && deps.DarwinSecondArchOutput.Valid() {
|
||||
fatOutputFile := outputFile
|
||||
outputFile = android.PathForModuleOut(ctx, "pre-fat", fileName)
|
||||
transformDarwinUniversalBinary(ctx, fatOutputFile, outputFile, deps.DarwinSecondArchOutput.Path())
|
||||
}
|
||||
|
||||
// Optimize out relinking against shared libraries whose interface hasn't changed by
|
||||
// depending on a table of contents file instead of the library itself.
|
||||
tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
|
||||
|
Reference in New Issue
Block a user