// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cc // This file generates the final rules for compiling all C/C++. All properties related to // compiling should have been translated into builderFlags or another argument to the Transform* // functions. import ( "android/soong/common" "fmt" "runtime" "strconv" "path/filepath" "strings" "github.com/google/blueprint" ) const ( objectExtension = ".o" staticLibraryExtension = ".a" ) var ( pctx = common.NewPackageContext("android/soong/cc") cc = pctx.StaticRule("cc", blueprint.RuleParams{ Depfile: "${out}.d", Deps: blueprint.DepsGCC, Command: "$relPwd $ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in", CommandDeps: []string{"$ccCmd"}, Description: "cc $out", }, "ccCmd", "cFlags") ld = pctx.StaticRule("ld", blueprint.RuleParams{ Command: "$ldCmd ${ldDirFlags} ${crtBegin} @${out}.rsp " + "${libFlags} ${crtEnd} -o ${out} ${ldFlags}", CommandDeps: []string{"$ldCmd"}, Description: "ld $out", Rspfile: "${out}.rsp", RspfileContent: "${in}", }, "ldCmd", "ldDirFlags", "crtBegin", "libFlags", "crtEnd", "ldFlags") partialLd = pctx.StaticRule("partialLd", blueprint.RuleParams{ Command: "$ldCmd -nostdlib -Wl,-r ${in} -o ${out} ${ldFlags}", CommandDeps: []string{"$ldCmd"}, Description: "partialLd $out", }, "ldCmd", "ldFlags") ar = pctx.StaticRule("ar", blueprint.RuleParams{ Command: "rm -f ${out} && $arCmd $arFlags $out @${out}.rsp", CommandDeps: []string{"$arCmd"}, Description: "ar $out", Rspfile: "${out}.rsp", RspfileContent: "${in}", }, "arCmd", "arFlags") darwinAr = pctx.StaticRule("darwinAr", blueprint.RuleParams{ Command: "rm -f ${out} && ${macArPath} $arFlags $out $in", CommandDeps: []string{"${macArPath}"}, Description: "ar $out", }, "arFlags") darwinAppendAr = pctx.StaticRule("darwinAppendAr", blueprint.RuleParams{ Command: "cp -f ${inAr} ${out}.tmp && ${macArPath} $arFlags ${out}.tmp $in && mv ${out}.tmp ${out}", CommandDeps: []string{"${macArPath}"}, Description: "ar $out", }, "arFlags", "inAr") darwinStrip = pctx.StaticRule("darwinStrip", blueprint.RuleParams{ Command: "${macStripPath} -u -r -o $out $in", CommandDeps: []string{"${macStripPath}"}, Description: "strip $out", }) prefixSymbols = pctx.StaticRule("prefixSymbols", blueprint.RuleParams{ Command: "$objcopyCmd --prefix-symbols=${prefix} ${in} ${out}", CommandDeps: []string{"$objcopyCmd"}, Description: "prefixSymbols $out", }, "objcopyCmd", "prefix") stripPath = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh") strip = pctx.StaticRule("strip", blueprint.RuleParams{ Depfile: "${out}.d", Deps: blueprint.DepsGCC, Command: "CROSS_COMPILE=$crossCompile $stripPath ${args} -i ${in} -o ${out} -d ${out}.d", CommandDeps: []string{"$stripPath"}, Description: "strip $out", }, "args", "crossCompile") copyGccLibPath = pctx.SourcePathVariable("copyGccLibPath", "build/soong/scripts/copygcclib.sh") copyGccLib = pctx.StaticRule("copyGccLib", blueprint.RuleParams{ Depfile: "${out}.d", Deps: blueprint.DepsGCC, Command: "$copyGccLibPath $out $ccCmd $cFlags -print-file-name=${libName}", CommandDeps: []string{"$copyGccLibPath", "$ccCmd"}, Description: "copy gcc $out", }, "ccCmd", "cFlags", "libName") ) func init() { // We run gcc/clang with PWD=/proc/self/cwd to remove $TOP from the // debug output. That way two builds in two different directories will // create the same output. if runtime.GOOS != "darwin" { pctx.StaticVariable("relPwd", "PWD=/proc/self/cwd") } else { // Darwin doesn't have /proc pctx.StaticVariable("relPwd", "") } } type builderFlags struct { globalFlags string asFlags string cFlags string conlyFlags string cppFlags string ldFlags string libFlags string yaccFlags string nocrt bool toolchain Toolchain clang bool stripKeepSymbols bool stripKeepMiniDebugInfo bool stripAddGnuDebuglink bool } // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files func TransformSourceToObj(ctx common.AndroidModuleContext, subdir string, srcFiles common.Paths, flags builderFlags, deps common.Paths) (objFiles common.Paths) { objFiles = make(common.Paths, len(srcFiles)) cflags := flags.globalFlags + " " + flags.cFlags + " " + flags.conlyFlags cppflags := flags.globalFlags + " " + flags.cFlags + " " + flags.cppFlags asflags := flags.globalFlags + " " + flags.asFlags if flags.clang { cflags += " ${noOverrideClangGlobalCflags}" cppflags += " ${noOverrideClangGlobalCflags}" } else { cflags += " ${noOverrideGlobalCflags}" cppflags += " ${noOverrideGlobalCflags}" } for i, srcFile := range srcFiles { objFile := common.ObjPathWithExt(ctx, srcFile, subdir, "o") objFiles[i] = objFile var moduleCflags string var ccCmd string switch srcFile.Ext() { case ".S", ".s": ccCmd = "gcc" moduleCflags = asflags case ".c": ccCmd = "gcc" moduleCflags = cflags case ".cpp", ".cc": ccCmd = "g++" moduleCflags = cppflags default: ctx.ModuleErrorf("File %s has unknown extension", srcFile) continue } if flags.clang { switch ccCmd { case "gcc": ccCmd = "clang" case "g++": ccCmd = "clang++" default: panic("unrecoginzied ccCmd") } ccCmd = "${clangBin}/" + ccCmd } else { ccCmd = gccCmd(flags.toolchain, ccCmd) } ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: cc, Output: objFile, Input: srcFile, OrderOnly: deps, Args: map[string]string{ "cFlags": moduleCflags, "ccCmd": ccCmd, }, }) } return objFiles } // Generate a rule for compiling multiple .o files to a static library (.a) func TransformObjToStaticLib(ctx common.AndroidModuleContext, objFiles common.Paths, flags builderFlags, outputFile common.ModuleOutPath) { arCmd := gccCmd(flags.toolchain, "ar") arFlags := "crsPD" ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: ar, Output: outputFile, Inputs: objFiles, Args: map[string]string{ "arFlags": arFlags, "arCmd": arCmd, }, }) } // Generate a rule for compiling multiple .o files to a static library (.a) on // darwin. The darwin ar tool doesn't support @file for list files, and has a // very small command line length limit, so we have to split the ar into multiple // steps, each appending to the previous one. func TransformDarwinObjToStaticLib(ctx common.AndroidModuleContext, objFiles common.Paths, flags builderFlags, outputPath common.ModuleOutPath) { arFlags := "cqs" // ARG_MAX on darwin is 262144, use half that to be safe objFilesLists, err := splitListForSize(objFiles.Strings(), 131072) if err != nil { ctx.ModuleErrorf("%s", err.Error()) } outputFile := outputPath.String() var in, out string for i, l := range objFilesLists { in = out out = outputFile if i != len(objFilesLists)-1 { out += "." + strconv.Itoa(i) } if in == "" { ctx.Build(pctx, blueprint.BuildParams{ Rule: darwinAr, Outputs: []string{out}, Inputs: l, Args: map[string]string{ "arFlags": arFlags, }, }) } else { ctx.Build(pctx, blueprint.BuildParams{ Rule: darwinAppendAr, Outputs: []string{out}, Inputs: l, Implicits: []string{in}, Args: map[string]string{ "arFlags": arFlags, "inAr": in, }, }) } } } // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries, // and shared libraires, to a shared library (.so) or dynamic executable func TransformObjToDynamicBinary(ctx common.AndroidModuleContext, objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps common.Paths, crtBegin, crtEnd common.OptionalPath, groupLate bool, flags builderFlags, outputFile common.WritablePath) { var ldCmd string if flags.clang { ldCmd = "${clangBin}/clang++" } else { ldCmd = gccCmd(flags.toolchain, "g++") } var ldDirs []string var libFlagsList []string if len(flags.libFlags) > 0 { libFlagsList = append(libFlagsList, flags.libFlags) } if len(wholeStaticLibs) > 0 { if ctx.Host() && ctx.Darwin() { libFlagsList = append(libFlagsList, common.JoinWithPrefix(wholeStaticLibs.Strings(), "-force_load ")) } else { libFlagsList = append(libFlagsList, "-Wl,--whole-archive ") libFlagsList = append(libFlagsList, wholeStaticLibs.Strings()...) libFlagsList = append(libFlagsList, "-Wl,--no-whole-archive ") } } libFlagsList = append(libFlagsList, staticLibs.Strings()...) if groupLate && len(lateStaticLibs) > 0 { libFlagsList = append(libFlagsList, "-Wl,--start-group") } libFlagsList = append(libFlagsList, lateStaticLibs.Strings()...) if groupLate && len(lateStaticLibs) > 0 { libFlagsList = append(libFlagsList, "-Wl,--end-group") } for _, lib := range sharedLibs { dir, file := filepath.Split(lib.String()) if !strings.HasPrefix(file, "lib") { panic("shared library " + lib.String() + " does not start with lib") } if !strings.HasSuffix(file, flags.toolchain.ShlibSuffix()) { panic("shared library " + lib.String() + " does not end with " + flags.toolchain.ShlibSuffix()) } libFlagsList = append(libFlagsList, "-l"+strings.TrimSuffix(strings.TrimPrefix(file, "lib"), flags.toolchain.ShlibSuffix())) ldDirs = append(ldDirs, dir) } deps = append(deps, sharedLibs...) deps = append(deps, staticLibs...) deps = append(deps, lateStaticLibs...) deps = append(deps, wholeStaticLibs...) if crtBegin.Valid() { deps = append(deps, crtBegin.Path(), crtEnd.Path()) } ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: ld, Output: outputFile, Inputs: objFiles, Implicits: deps, Args: map[string]string{ "ldCmd": ldCmd, "ldDirFlags": ldDirsToFlags(ldDirs), "crtBegin": crtBegin.String(), "libFlags": strings.Join(libFlagsList, " "), "ldFlags": flags.ldFlags, "crtEnd": crtEnd.String(), }, }) } // Generate a rule for compiling multiple .o files to a .o using ld partial linking func TransformObjsToObj(ctx common.AndroidModuleContext, objFiles common.Paths, flags builderFlags, outputFile common.WritablePath) { var ldCmd string if flags.clang { ldCmd = "${clangBin}clang++" } else { ldCmd = gccCmd(flags.toolchain, "g++") } ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: partialLd, Output: outputFile, Inputs: objFiles, Args: map[string]string{ "ldCmd": ldCmd, "ldFlags": flags.ldFlags, }, }) } // Generate a rule for runing objcopy --prefix-symbols on a binary func TransformBinaryPrefixSymbols(ctx common.AndroidModuleContext, prefix string, inputFile common.Path, flags builderFlags, outputFile common.WritablePath) { objcopyCmd := gccCmd(flags.toolchain, "objcopy") ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: prefixSymbols, Output: outputFile, Input: inputFile, Args: map[string]string{ "objcopyCmd": objcopyCmd, "prefix": prefix, }, }) } func TransformStrip(ctx common.AndroidModuleContext, inputFile common.Path, outputFile common.WritablePath, flags builderFlags) { crossCompile := gccCmd(flags.toolchain, "") args := "" if flags.stripAddGnuDebuglink { args += " --add-gnu-debuglink" } if flags.stripKeepMiniDebugInfo { args += " --keep-mini-debug-info" } if flags.stripKeepSymbols { args += " --keep-symbols" } ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: strip, Output: outputFile, Input: inputFile, Args: map[string]string{ "crossCompile": crossCompile, "args": args, }, }) } func TransformDarwinStrip(ctx common.AndroidModuleContext, inputFile common.Path, outputFile common.WritablePath) { ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: darwinStrip, Output: outputFile, Input: inputFile, }) } func CopyGccLib(ctx common.AndroidModuleContext, libName string, flags builderFlags, outputFile common.WritablePath) { ctx.ModuleBuild(pctx, common.ModuleBuildParams{ Rule: copyGccLib, Output: outputFile, Args: map[string]string{ "ccCmd": gccCmd(flags.toolchain, "gcc"), "cFlags": flags.globalFlags, "libName": libName, }, }) } func gccCmd(toolchain Toolchain, cmd string) string { return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd) } func splitListForSize(list []string, limit int) (lists [][]string, err error) { var i int start := 0 bytes := 0 for i = range list { l := len(list[i]) if l > limit { return nil, fmt.Errorf("list element greater than size limit (%d)", limit) } if bytes+l > limit { lists = append(lists, list[start:i]) start = i bytes = 0 } bytes += l + 1 // count a space between each list element } lists = append(lists, list[start:]) totalLen := 0 for _, l := range lists { totalLen += len(l) } if totalLen != len(list) { panic(fmt.Errorf("Failed breaking up list, %d != %d", len(list), totalLen)) } return lists, nil }