diff options
author | Fabien Sanglard <sanglardf@google.com> | 2017-01-10 16:21:22 -0800 |
---|---|---|
committer | Fabien Sanglard <sanglardf@google.com> | 2017-02-07 09:43:02 -0800 |
commit | d61f1f455917ed6affe9361c0f89ddfcbfa50d02 (patch) | |
tree | a2739ccbe850c8383eb4f8b9b261604a49e11286 /cc | |
parent | 6d34b308ff8c87425163c3dbc864aa388f18d468 (diff) | |
download | build_soong-d61f1f455917ed6affe9361c0f89ddfcbfa50d02.tar.gz build_soong-d61f1f455917ed6affe9361c0f89ddfcbfa50d02.tar.bz2 build_soong-d61f1f455917ed6affe9361c0f89ddfcbfa50d02.zip |
Add support for CMakefile generation
Test: Manually generated CMakeLists.txt for gui/ui/aapt2.
Change-Id: I7dedc300c1e50b8e39bc58091b650c0bbe2c62da
Diffstat (limited to 'cc')
-rw-r--r-- | cc/cc.go | 17 | ||||
-rw-r--r-- | cc/cmakelists.go | 343 | ||||
-rw-r--r-- | cc/compiler.go | 13 |
3 files changed, 366 insertions, 7 deletions
@@ -269,6 +269,9 @@ type Module struct { cachedToolchain config.Toolchain subAndroidMkOnce map[subAndroidMkProvider]bool + + // Flags used to compile this module + flags Flags } func (c *Module) Init() (blueprint.Module, []interface{}) { @@ -462,6 +465,13 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { flags.CppFlags, _ = filterList(flags.CppFlags, config.IllegalFlags) flags.ConlyFlags, _ = filterList(flags.ConlyFlags, config.IllegalFlags) + deps := c.depsToPaths(ctx) + if ctx.Failed() { + return + } + flags.GlobalFlags = append(flags.GlobalFlags, deps.Flags...) + c.flags = flags + // Optimization to reduce size of build.ninja // Replace the long list of flags for each file with a module-local variable ctx.Variable(pctx, "cflags", strings.Join(flags.CFlags, " ")) @@ -471,13 +481,6 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { flags.CppFlags = []string{"$cppflags"} flags.AsFlags = []string{"$asflags"} - deps := c.depsToPaths(ctx) - if ctx.Failed() { - return - } - - flags.GlobalFlags = append(flags.GlobalFlags, deps.Flags...) - var objs Objects if c.compiler != nil { objs = c.compiler.compile(ctx, flags, deps) diff --git a/cc/cmakelists.go b/cc/cmakelists.go new file mode 100644 index 00000000..f8273cf1 --- /dev/null +++ b/cc/cmakelists.go @@ -0,0 +1,343 @@ +package cc + +import ( + "fmt" + + "android/soong/android" + "android/soong/cc/config" + "github.com/google/blueprint" + "os" + "path" + "path/filepath" + "strings" +) + +// This singleton generates CMakeLists.txt files. It does so for each blueprint Android.bp resulting in a cc.Module +// when either make, mm, mma, mmm or mmma is called. CMakeLists.txt files are generated in a separate folder +// structure (see variable CLionOutputProjectsDirectory for root). + +func init() { + android.RegisterSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton) +} + +func cMakeListsGeneratorSingleton() blueprint.Singleton { + return &cmakelistsGeneratorSingleton{} +} + +type cmakelistsGeneratorSingleton struct{} + +const ( + cMakeListsFilename = "CMakeLists.txt" + cLionAggregateProjectsDirectory = "development" + string(os.PathSeparator) + "ide" + string(os.PathSeparator) + "clion" + cLionOutputProjectsDirectory = "out" + string(os.PathSeparator) + cLionAggregateProjectsDirectory + minimumCMakeVersionSupported = "3.5" + + // Environment variables used to modify behavior of this singleton. + envVariableGenerateCMakeLists = "SOONG_GEN_CMAKEFILES" + envVariableGenerateDebugInfo = "SOONG_GEN_CMAKEFILES_DEBUG" + envVariableTrue = "1" +) + +// Instruct generator to trace how header include path and flags were generated. +// This is done to ease investigating bug reports. +var outputDebugInfo = false + +func (c *cmakelistsGeneratorSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) { + if getEnvVariable(envVariableGenerateCMakeLists, ctx) != envVariableTrue { + return + } + + outputDebugInfo = (getEnvVariable(envVariableGenerateDebugInfo, ctx) == envVariableTrue) + + ctx.VisitAllModules(func(module blueprint.Module) { + if ccModule, ok := module.(*Module); ok { + if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok { + generateCLionProject(compiledModule, ctx, ccModule) + } + } + }) + + // Link all handmade CMakeLists.txt aggregate from + // BASE/development/ide/clion to + // BASE/out/development/ide/clion. + dir := filepath.Join(getAndroidSrcRootDirectory(ctx), cLionAggregateProjectsDirectory) + filepath.Walk(dir, linkAggregateCMakeListsFiles) + + return +} + +func getEnvVariable(name string, ctx blueprint.SingletonContext) string { + // Using android.Config.Getenv instead of os.getEnv to guarantee soong will + // re-run in case this environment variable changes. + return ctx.Config().(android.Config).Getenv(name) +} + +func exists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return true +} + +func linkAggregateCMakeListsFiles(path string, info os.FileInfo, err error) error { + + if info == nil { + return nil + } + + dst := strings.Replace(path, cLionAggregateProjectsDirectory, cLionOutputProjectsDirectory, 1) + if info.IsDir() { + // This is a directory to create + os.MkdirAll(dst, os.ModePerm) + } else { + // This is a file to link + os.Remove(dst) + os.Symlink(path, dst) + } + return nil +} + +func generateCLionProject(compiledModule CompiledInterface, ctx blueprint.SingletonContext, ccModule *Module) { + srcs := compiledModule.Srcs() + if len(srcs) == 0 { + return + } + + // Ensure the directory hosting the cmakelists.txt exists + clionproject_location := getCMakeListsForModule(ccModule, ctx) + projectDir := path.Dir(clionproject_location) + os.MkdirAll(projectDir, os.ModePerm) + + // Create cmakelists.txt + f, _ := os.Create(filepath.Join(projectDir, cMakeListsFilename)) + defer f.Close() + + // Header. + f.WriteString("# THIS FILE WAS AUTOMATICALY GENERATED!\n") + f.WriteString("# ANY MODIFICATION WILL BE OVERWRITTEN!\n\n") + f.WriteString("# To improve project view in Clion :\n") + f.WriteString("# Tools > CMake > Change Project Root \n\n") + f.WriteString(fmt.Sprintf("cmake_minimum_required(VERSION %s)\n", minimumCMakeVersionSupported)) + f.WriteString(fmt.Sprintf("project(%s)\n", ccModule.ModuleBase.Name())) + f.WriteString(fmt.Sprintf("set(ANDROID_ROOT %s)\n\n", getAndroidSrcRootDirectory(ctx))) + + if ccModule.flags.Clang { + pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/") + f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang")) + f.WriteString(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "clang++")) + } else { + toolchain := config.FindToolchain(ccModule.Os(), ccModule.Arch()) + root, _ := evalVariable(ctx, toolchain.GccRoot()) + triple, _ := evalVariable(ctx, toolchain.GccTriple()) + pathToCC := filepath.Join(root, "bin", triple+"-") + f.WriteString(fmt.Sprintf("set(CMAKE_C_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "gcc")) + f.WriteString(fmt.Sprintf("set(CMAKE_CXX_COMPILER \"%s%s\")\n", buildCMakePath(pathToCC), "g++")) + } + // Add all sources to the project. + f.WriteString("list(APPEND\n") + f.WriteString(" SOURCE_FILES\n") + for _, src := range srcs { + f.WriteString(fmt.Sprintf(" ${ANDROID_ROOT}/%s\n", src.String())) + } + f.WriteString(")\n") + + // Add all header search path and compiler parameters (-D, -W, -f, -XXXX) + f.WriteString("\n# GLOBAL FLAGS:\n") + globalParameters := parseCompilerParameters(ccModule.flags.GlobalFlags, ctx, f) + translateToCMake(globalParameters, f, true, true) + + f.WriteString("\n# CFLAGS:\n") + cParameters := parseCompilerParameters(ccModule.flags.CFlags, ctx, f) + translateToCMake(cParameters, f, true, true) + + f.WriteString("\n# C ONLY FLAGS:\n") + cOnlyParameters := parseCompilerParameters(ccModule.flags.ConlyFlags, ctx, f) + translateToCMake(cOnlyParameters, f, true, false) + + f.WriteString("\n# CPP FLAGS:\n") + cppParameters := parseCompilerParameters(ccModule.flags.CppFlags, ctx, f) + translateToCMake(cppParameters, f, false, true) + + // Add project executable. + f.WriteString(fmt.Sprintf("\nadd_executable(%s ${SOURCE_FILES})\n", ccModule.ModuleBase.Name())) +} + +func translateToCMake(c compilerParameters, f *os.File, cflags bool, cppflags bool) { + writeAllSystemDirectories(c.systemHeaderSearchPath, f) + writeAllIncludeDirectories(c.headerSearchPath, f) + if cflags { + writeAllFlags(c.flags, f, "CMAKE_C_FLAGS") + } + + if cppflags { + writeAllFlags(c.flags, f, "CMAKE_CXX_FLAGS") + } + if c.sysroot != "" { + f.WriteString(fmt.Sprintf("include_directories(SYSTEM \"%s\")\n", buildCMakePath(path.Join(c.sysroot, "usr", "include")))) + } + +} + +func buildCMakePath(p string) string { + if path.IsAbs(p) { + return p + } + return fmt.Sprintf("${ANDROID_ROOT}/%s", p) +} + +func writeAllIncludeDirectories(includes map[string]bool, f *os.File) { + for include := range includes { + f.WriteString(fmt.Sprintf("include_directories(\"%s\")\n", buildCMakePath(include))) + } +} + +func writeAllSystemDirectories(includes map[string]bool, f *os.File) { + for include := range includes { + f.WriteString(fmt.Sprintf("include_directories(SYSTEM \"%s\")\n", buildCMakePath(include))) + } +} + +func writeAllFlags(flags []string, f *os.File, tag string) { + for _, flag := range flags { + f.WriteString(fmt.Sprintf("set(%s \"${%s} %s\")\n", tag, tag, flag)) + } +} + +type parameterType int + +const ( + headerSearchPath parameterType = iota + variable + systemHeaderSearchPath + flag + systemRoot +) + +type compilerParameters struct { + headerSearchPath map[string]bool + systemHeaderSearchPath map[string]bool + flags []string + sysroot string +} + +func makeCompilerParameters() compilerParameters { + return compilerParameters{ + headerSearchPath: make(map[string]bool), + systemHeaderSearchPath: make(map[string]bool), + flags: make([]string, 0), + sysroot: "", + } +} + +func categorizeParameter(parameter string) parameterType { + if strings.HasPrefix(parameter, "-I") { + return headerSearchPath + } + if strings.HasPrefix(parameter, "$") { + return variable + } + if strings.HasPrefix(parameter, "-isystem") { + return systemHeaderSearchPath + } + if strings.HasPrefix(parameter, "-isysroot") { + return systemRoot + } + if strings.HasPrefix(parameter, "--sysroot") { + return systemRoot + } + return flag +} + +func parseCompilerParameters(params []string, ctx blueprint.SingletonContext, f *os.File) compilerParameters { + var compilerParameters = makeCompilerParameters() + + for i, str := range params { + f.WriteString(fmt.Sprintf("# Raw param [%d] = '%s'\n", i, str)) + } + + for i := 0; i < len(params); i++ { + param := params[i] + if param == "" { + continue + } + + switch categorizeParameter(param) { + case headerSearchPath: + compilerParameters.headerSearchPath[strings.TrimPrefix(param, "-I")] = true + case variable: + if evaluated, error := evalVariable(ctx, param); error == nil { + if outputDebugInfo { + f.WriteString(fmt.Sprintf("# variable %s = '%s'\n", param, evaluated)) + } + + paramsFromVar := parseCompilerParameters(strings.Split(evaluated, " "), ctx, f) + concatenateParams(&compilerParameters, paramsFromVar) + + } else { + if outputDebugInfo { + f.WriteString(fmt.Sprintf("# variable %s could NOT BE RESOLVED\n", param)) + } + } + case systemHeaderSearchPath: + if i < len(params)-1 { + compilerParameters.systemHeaderSearchPath[params[i+1]] = true + } else if outputDebugInfo { + f.WriteString("# Found a header search path marker with no path") + } + i = i + 1 + case flag: + compilerParameters.flags = append(compilerParameters.flags, param) + case systemRoot: + if i < len(params)-1 { + compilerParameters.sysroot = params[i+1] + } else if outputDebugInfo { + f.WriteString("# Found a system root path marker with no path") + } + i = i + 1 + } + } + return compilerParameters +} + +func concatenateParams(c1 *compilerParameters, c2 compilerParameters) { + concatenateMaps(c1.headerSearchPath, c2.headerSearchPath) + concatenateMaps(c1.systemHeaderSearchPath, c2.systemHeaderSearchPath) + if c2.sysroot != "" { + c1.sysroot = c2.sysroot + } + c1.flags = append(c1.flags, c2.flags...) +} + +func evalVariable(ctx blueprint.SingletonContext, str string) (string, error) { + evaluated, err := ctx.Eval(pctx, str) + if err == nil { + return evaluated, nil + } + return "", err +} + +// Concatenate two maps into one. Results are stored in first operand. +func concatenateMaps(map1 map[string]bool, map2 map[string]bool) { + for key, value := range map2 { + map1[key] = value + } +} + +func getCMakeListsForModule(module *Module, ctx blueprint.SingletonContext) string { + return filepath.Join(getAndroidSrcRootDirectory(ctx), + cLionOutputProjectsDirectory, + path.Dir(ctx.BlueprintFile(module)), + module.ModuleBase.Name()+"-"+ + module.ModuleBase.Arch().ArchType.Name+"-"+ + module.ModuleBase.Os().Name, + cMakeListsFilename) +} + +func getAndroidSrcRootDirectory(ctx blueprint.SingletonContext) string { + srcPath, _ := filepath.Abs(android.PathForSource(ctx).String()) + return srcPath +} diff --git a/cc/compiler.go b/cc/compiler.go index e962949b..dc594e30 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -123,10 +123,20 @@ type baseCompiler struct { Properties BaseCompilerProperties Proto ProtoProperties deps android.Paths + srcs android.Paths + flags builderFlags } var _ compiler = (*baseCompiler)(nil) +type CompiledInterface interface { + Srcs() android.Paths +} + +func (compiler *baseCompiler) Srcs() android.Paths { + return compiler.srcs +} + func (compiler *baseCompiler) appendCflags(flags []string) { compiler.Properties.Cflags = append(compiler.Properties.Cflags, flags...) } @@ -429,6 +439,9 @@ func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathD compiler.deps = pathDeps + // Save src, buildFlags and context + compiler.srcs = srcs + // Compile files listed in c.Properties.Srcs into objects objs := compileObjs(ctx, buildFlags, "", srcs, compiler.deps) |