Add support for CMakefile generation
Test: Manually generated CMakeLists.txt for gui/ui/aapt2. Change-Id: I7dedc300c1e50b8e39bc58091b650c0bbe2c62da
This commit is contained in:
parent
6d34b308ff
commit
d61f1f4559
|
@ -129,6 +129,7 @@ bootstrap_go_package {
|
|||
"cc/tidy.go",
|
||||
"cc/util.go",
|
||||
|
||||
"cc/cmakelists.go",
|
||||
"cc/compiler.go",
|
||||
"cc/installer.go",
|
||||
"cc/linker.go",
|
||||
|
|
|
@ -27,10 +27,6 @@ import (
|
|||
// compare the contents of the environment variables, rewriting the file if necessary to cause
|
||||
// a manifest regeneration.
|
||||
|
||||
func init() {
|
||||
RegisterSingletonType("env", EnvSingleton)
|
||||
}
|
||||
|
||||
func EnvSingleton() blueprint.Singleton {
|
||||
return &envSingleton{}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ func NewContext() *blueprint.Context {
|
|||
handle.Parallel()
|
||||
}
|
||||
}
|
||||
ctx.RegisterSingletonType("env", EnvSingleton)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
|
17
cc/cc.go
17
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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue