aboutsummaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
authorFabien Sanglard <sanglardf@google.com>2017-01-10 16:21:22 -0800
committerFabien Sanglard <sanglardf@google.com>2017-02-07 09:43:02 -0800
commitd61f1f455917ed6affe9361c0f89ddfcbfa50d02 (patch)
treea2739ccbe850c8383eb4f8b9b261604a49e11286 /cc
parent6d34b308ff8c87425163c3dbc864aa388f18d468 (diff)
downloadbuild_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.go17
-rw-r--r--cc/cmakelists.go343
-rw-r--r--cc/compiler.go13
3 files changed, 366 insertions, 7 deletions
diff --git a/cc/cc.go b/cc/cc.go
index 8bf14678..3824b451 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -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)