diff options
-rw-r--r-- | android/arch.go | 2 | ||||
-rw-r--r-- | android/config.go | 16 | ||||
-rw-r--r-- | android/env.go | 16 | ||||
-rw-r--r-- | android/package_ctx.go | 6 | ||||
-rw-r--r-- | android/paths.go | 68 | ||||
-rw-r--r-- | androidmk/cmd/androidmk/android.go | 2 | ||||
-rw-r--r-- | cc/config/arm64_device.go | 5 | ||||
-rw-r--r-- | cc/config/arm_device.go | 23 | ||||
-rw-r--r-- | cc/config/global.go | 6 | ||||
-rw-r--r-- | cc/config/x86_darwin_host.go | 24 | ||||
-rw-r--r-- | ui/build/Android.bp | 1 | ||||
-rw-r--r-- | ui/build/build.go | 110 | ||||
-rw-r--r-- | ui/build/config.go | 68 | ||||
-rw-r--r-- | ui/build/exec.go | 42 | ||||
-rw-r--r-- | ui/build/java.go | 156 | ||||
-rw-r--r-- | ui/build/make.go | 5 | ||||
-rw-r--r-- | ui/tracer/tracer.go | 2 |
17 files changed, 479 insertions, 73 deletions
diff --git a/android/arch.go b/android/arch.go index 51fc444e..effd5a6b 100644 --- a/android/arch.go +++ b/android/arch.go @@ -882,10 +882,12 @@ func getMegaDeviceConfig() []archConfig { {"arm", "armv7-a-neon", "cortex-a15", []string{"armeabi-v7a"}}, {"arm", "armv7-a-neon", "cortex-a53", []string{"armeabi-v7a"}}, {"arm", "armv7-a-neon", "cortex-a53.a57", []string{"armeabi-v7a"}}, + {"arm", "armv7-a-neon", "cortex-a73", []string{"armeabi-v7a"}}, {"arm", "armv7-a-neon", "denver", []string{"armeabi-v7a"}}, {"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}}, {"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}}, {"arm64", "armv8-a", "cortex-a53", []string{"arm64-v8a"}}, + {"arm64", "armv8-a", "cortex-a73", []string{"arm64-v8a"}}, {"arm64", "armv8-a", "denver64", []string{"arm64-v8a"}}, {"arm64", "armv8-a", "kryo", []string{"arm64-v8a"}}, {"mips", "mips32-fp", "", []string{"mips"}}, diff --git a/android/config.go b/android/config.go index 8be16cfd..b83ffd4a 100644 --- a/android/config.go +++ b/android/config.go @@ -249,6 +249,20 @@ func (c *config) BlueprintToolLocation() string { return filepath.Join(c.buildDir, "host", c.PrebuiltOS(), "bin") } +// HostSystemTool looks for non-hermetic tools from the system we're running on. +// Generally shouldn't be used, but useful to find the XCode SDK, etc. +func (c *config) HostSystemTool(name string) string { + for _, dir := range filepath.SplitList(c.Getenv("PATH")) { + path := filepath.Join(dir, name) + if s, err := os.Stat(path); err != nil { + continue + } else if m := s.Mode(); !s.IsDir() && m&0111 != 0 { + return path + } + } + return name +} + // PrebuiltOS returns the name of the host OS used in prebuilts directories func (c *config) PrebuiltOS() string { switch runtime.GOOS { @@ -289,7 +303,7 @@ func (c *config) Getenv(key string) string { if c.envFrozen { panic("Cannot access new environment variables after envdeps are frozen") } - val = os.Getenv(key) + val, _ = originalEnv[key] c.envDeps[key] = val } return val diff --git a/android/env.go b/android/env.go index c7409e87..ec5794e3 100644 --- a/android/env.go +++ b/android/env.go @@ -15,6 +15,9 @@ package android import ( + "os" + "strings" + "android/soong/env" "github.com/google/blueprint" @@ -27,6 +30,19 @@ import ( // compare the contents of the environment variables, rewriting the file if necessary to cause // a manifest regeneration. +var originalEnv map[string]string + +func init() { + originalEnv = make(map[string]string) + for _, env := range os.Environ() { + idx := strings.IndexRune(env, '=') + if idx != -1 { + originalEnv[env[:idx]] = env[idx+1:] + } + } + os.Clearenv() +} + func EnvSingleton() blueprint.Singleton { return &envSingleton{} } diff --git a/android/package_ctx.go b/android/package_ctx.go index d9bb1090..53044039 100644 --- a/android/package_ctx.go +++ b/android/package_ctx.go @@ -120,16 +120,16 @@ func (p AndroidPackageContext) IntermediatesPathVariable(name, path string) blue }) } -// PrefixedPathsForOptionalSourceVariable returns a Variable whose value is the +// PrefixedExistentPathsForSourcesVariable returns a Variable whose value is the // list of present source paths prefixed with the supplied prefix. It may only // be called during a Go package's initialization - either from the init() // function or as part of a package-scoped variable's initialization. -func (p AndroidPackageContext) PrefixedPathsForOptionalSourceVariable( +func (p AndroidPackageContext) PrefixedExistentPathsForSourcesVariable( name, prefix string, paths []string) blueprint.Variable { return p.VariableFunc(name, func(config interface{}) (string, error) { ctx := &configErrorWrapper{p, config.(Config), []error{}} - paths := PathsForOptionalSource(ctx, "", paths) + paths := ExistentPathsForSources(ctx, "", paths) if len(ctx.errors) > 0 { return "", ctx.errors[0] } diff --git a/android/paths.go b/android/paths.go index 0d26dc02..a23dd74e 100644 --- a/android/paths.go +++ b/android/paths.go @@ -97,6 +97,8 @@ type Path interface { type WritablePath interface { Path + // the writablePath method doesn't directly do anything, + // but it allows a struct to distinguish between whether or not it implements the WritablePath interface writablePath() } @@ -137,7 +139,7 @@ func ResPathWithName(ctx ModuleContext, p Path, name string) ModuleResPath { if path, ok := p.(resPathProvider); ok { return path.resPathWithName(ctx, name) } - reportPathError(ctx, "Tried to create object file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) + reportPathError(ctx, "Tried to create res file from unsupported path: %s (%s)", reflect.TypeOf(p).Name(), p) return PathForModuleRes(ctx) } @@ -188,7 +190,7 @@ func PathsForSource(ctx PathContext, paths []string) Paths { ret := make(Paths, 0, len(paths)) intermediates := filepath.Join(modCtx.ModuleDir(), modCtx.ModuleName(), modCtx.ModuleSubDir(), "missing") for _, path := range paths { - p := OptionalPathForSource(ctx, intermediates, path) + p := ExistentPathForSource(ctx, intermediates, path) if p.Valid() { ret = append(ret, p.Path()) } else { @@ -205,13 +207,13 @@ func PathsForSource(ctx PathContext, paths []string) Paths { return ret } -// PathsForOptionalSource returns a list of Paths rooted from SrcDir that are +// ExistentPathsForSources returns a list of Paths rooted from SrcDir that are // found in the tree. If any are not found, they are omitted from the list, // and dependencies are added so that we're re-run when they are added. -func PathsForOptionalSource(ctx PathContext, intermediates string, paths []string) Paths { +func ExistentPathsForSources(ctx PathContext, intermediates string, paths []string) Paths { ret := make(Paths, 0, len(paths)) for _, path := range paths { - p := OptionalPathForSource(ctx, intermediates, path) + p := ExistentPathForSource(ctx, intermediates, path) if p.Valid() { ret = append(ret, p.Path()) } @@ -337,12 +339,11 @@ func safePathForSource(ctx PathContext, path string) SourcePath { return ret } -// PathForSource returns a SourcePath for the provided paths... (which are -// joined together with filepath.Join). This also validates that the path -// doesn't escape the source dir, or is contained in the build dir. On error, it -// will return a usable, but invalid SourcePath, and report a ModuleError. -func PathForSource(ctx PathContext, paths ...string) SourcePath { - p := validatePath(ctx, paths...) +// PathForSource joins the provided path components and validates that the result +// neither escapes the source dir nor is in the out dir. +// On error, it will return a usable, but invalid SourcePath, and report a ModuleError. +func PathForSource(ctx PathContext, pathComponents ...string) SourcePath { + p := validatePath(ctx, pathComponents...) ret := SourcePath{basePath{p, pathConfig(ctx), ""}} abs, err := filepath.Abs(ret.String()) @@ -368,16 +369,16 @@ func PathForSource(ctx PathContext, paths ...string) SourcePath { return ret } -// OptionalPathForSource returns an OptionalPath with the SourcePath if the +// ExistentPathForSource returns an OptionalPath with the SourcePath if the // path exists, or an empty OptionalPath if it doesn't exist. Dependencies are added // so that the ninja file will be regenerated if the state of the path changes. -func OptionalPathForSource(ctx PathContext, intermediates string, paths ...string) OptionalPath { - if len(paths) == 0 { +func ExistentPathForSource(ctx PathContext, intermediates string, pathComponents ...string) OptionalPath { + if len(pathComponents) == 0 { // For when someone forgets the 'intermediates' argument panic("Missing path(s)") } - p := validatePath(ctx, paths...) + p := validatePath(ctx, pathComponents...) path := SourcePath{basePath{p, pathConfig(ctx), ""}} abs, err := filepath.Abs(path.String()) @@ -483,12 +484,11 @@ type OutputPath struct { var _ Path = OutputPath{} -// PathForOutput returns an OutputPath for the provided paths... (which are -// joined together with filepath.Join). This also validates that the path -// does not escape the build dir. On error, it will return a usable, but invalid -// OutputPath, and report a ModuleError. -func PathForOutput(ctx PathContext, paths ...string) OutputPath { - path := validatePath(ctx, paths...) +// PathForOutput joins the provided paths and returns an OutputPath that is +// validated to not escape the build dir. +// On error, it will return a usable, but invalid OutputPath, and report a ModuleError. +func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { + path := validatePath(ctx, pathComponents...) return OutputPath{basePath{path, pathConfig(ctx), ""}} } @@ -597,7 +597,7 @@ func PathForVndkRefAbiDump(ctx ModuleContext, version, fileName string, vndkOrNd } refDumpFileStr := "prebuilts/abi-dumps/" + vndkOrNdkDir + "/" + version + "/" + archName + "/" + sourceOrBinaryDir + "/" + fileName + ext - return OptionalPathForSource(ctx, "", refDumpFileStr) + return ExistentPathForSource(ctx, "", refDumpFileStr) } // PathForModuleOut returns a Path representing the paths... under the module's @@ -647,8 +647,8 @@ var _ Path = ModuleObjPath{} // PathForModuleObj returns a Path representing the paths... under the module's // 'obj' directory. -func PathForModuleObj(ctx ModuleContext, paths ...string) ModuleObjPath { - p := validatePath(ctx, paths...) +func PathForModuleObj(ctx ModuleContext, pathComponents ...string) ModuleObjPath { + p := validatePath(ctx, pathComponents...) return ModuleObjPath{PathForModuleOut(ctx, "obj", p)} } @@ -662,14 +662,14 @@ var _ Path = ModuleResPath{} // PathForModuleRes returns a Path representing the paths... under the module's // 'res' directory. -func PathForModuleRes(ctx ModuleContext, paths ...string) ModuleResPath { - p := validatePath(ctx, paths...) +func PathForModuleRes(ctx ModuleContext, pathComponents ...string) ModuleResPath { + p := validatePath(ctx, pathComponents...) return ModuleResPath{PathForModuleOut(ctx, "res", p)} } // PathForModuleInstall returns a Path representing the install path for the // module appended with paths... -func PathForModuleInstall(ctx ModuleContext, paths ...string) OutputPath { +func PathForModuleInstall(ctx ModuleContext, pathComponents ...string) OutputPath { var outPaths []string if ctx.Device() { partition := "system" @@ -689,14 +689,14 @@ func PathForModuleInstall(ctx ModuleContext, paths ...string) OutputPath { if ctx.Debug() { outPaths = append([]string{"debug"}, outPaths...) } - outPaths = append(outPaths, paths...) + outPaths = append(outPaths, pathComponents...) return PathForOutput(ctx, outPaths...) } // validateSafePath validates a path that we trust (may contain ninja variables). // Ensures that each path component does not attempt to leave its component. -func validateSafePath(ctx PathContext, paths ...string) string { - for _, path := range paths { +func validateSafePath(ctx PathContext, pathComponents ...string) string { + for _, path := range pathComponents { path := filepath.Clean(path) if path == ".." || strings.HasPrefix(path, "../") || strings.HasPrefix(path, "/") { reportPathError(ctx, "Path is outside directory: %s", path) @@ -706,20 +706,20 @@ func validateSafePath(ctx PathContext, paths ...string) string { // TODO: filepath.Join isn't necessarily correct with embedded ninja // variables. '..' may remove the entire ninja variable, even if it // will be expanded to multiple nested directories. - return filepath.Join(paths...) + return filepath.Join(pathComponents...) } // validatePath validates that a path does not include ninja variables, and that // each path component does not attempt to leave its component. Returns a joined // version of each path component. -func validatePath(ctx PathContext, paths ...string) string { - for _, path := range paths { +func validatePath(ctx PathContext, pathComponents ...string) string { + for _, path := range pathComponents { if strings.Contains(path, "$") { reportPathError(ctx, "Path contains invalid character($): %s", path) return "" } } - return validateSafePath(ctx, paths...) + return validateSafePath(ctx, pathComponents...) } type testPath struct { diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go index 8801b34b..78395c33 100644 --- a/androidmk/cmd/androidmk/android.go +++ b/androidmk/cmd/androidmk/android.go @@ -107,7 +107,7 @@ func init() { "LOCAL_DX_FLAGS": "dxflags", "LOCAL_JAVA_LIBRARIES": "java_libs", "LOCAL_STATIC_JAVA_LIBRARIES": "java_static_libs", - "LOCAL_AIDL_INCLUDES": "aidl_includes", + "LOCAL_AIDL_INCLUDES": "aidl.include_dirs", "LOCAL_AAPT_FLAGS": "aaptflags", "LOCAL_PACKAGE_SPLITS": "package_splits", "LOCAL_COMPATIBILITY_SUITE": "test_suites", diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go index f387ddfb..a371cf69 100644 --- a/cc/config/arm64_device.go +++ b/cc/config/arm64_device.go @@ -93,7 +93,8 @@ const ( func init() { android.RegisterArchVariants(android.Arm64, "armv8_a", - "cortex_a53", + "cortex-a53", + "cortex-a73", "kryo", "denver64") @@ -129,12 +130,14 @@ var ( arm64CpuVariantCflagsVar = map[string]string{ "": "", "cortex-a53": "${config.Arm64CortexA53Cflags}", + "cortex-a73": "${config.Arm64CortexA53Cflags}", "kryo": "${config.Arm64KryoCflags}", } arm64ClangCpuVariantCflagsVar = map[string]string{ "": "", "cortex-a53": "${config.Arm64ClangCortexA53Cflags}", + "cortex-a73": "${config.Arm64ClangCortexA53Cflags}", "kryo": "${config.Arm64ClangKryoCflags}", } ) diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go index 94627e7d..ee9e042b 100644 --- a/cc/config/arm_device.go +++ b/cc/config/arm_device.go @@ -127,6 +127,15 @@ var ( // better solution comes around. See Bug 27340895 "-D__ARM_FEATURE_LPAE=1", }, + "cortex-a53": []string{ + "-mcpu=cortex-a53", + "-mfpu=neon-fp-armv8", + // Fake an ARM compiler flag as these processors support LPAE which GCC/clang + // don't advertise. + // TODO This is a hack and we need to add it for each processor that supports LPAE until some + // better solution comes around. See Bug 27340895 + "-D__ARM_FEATURE_LPAE=1", + }, "krait": []string{ "-mcpu=cortex-a15", "-mfpu=neon-vfpv4", @@ -169,6 +178,7 @@ func init() { "cortex-a15", "cortex-a53", "cortex-a53-a57", + "cortex-a73", "krait", "kryo", "denver") @@ -207,6 +217,7 @@ func init() { pctx.StaticVariable("ArmCortexA7Cflags", strings.Join(armCpuVariantCflags["cortex-a7"], " ")) pctx.StaticVariable("ArmCortexA8Cflags", strings.Join(armCpuVariantCflags["cortex-a8"], " ")) pctx.StaticVariable("ArmCortexA15Cflags", strings.Join(armCpuVariantCflags["cortex-a15"], " ")) + pctx.StaticVariable("ArmCortexA53Cflags", strings.Join(armCpuVariantCflags["cortex-a53"], " ")) pctx.StaticVariable("ArmKraitCflags", strings.Join(armCpuVariantCflags["krait"], " ")) pctx.StaticVariable("ArmKryoCflags", strings.Join(armCpuVariantCflags["kryo"], " ")) @@ -237,6 +248,8 @@ func init() { strings.Join(armClangCpuVariantCflags["cortex-a8"], " ")) pctx.StaticVariable("ArmClangCortexA15Cflags", strings.Join(armClangCpuVariantCflags["cortex-a15"], " ")) + pctx.StaticVariable("ArmClangCortexA53Cflags", + strings.Join(armClangCpuVariantCflags["cortex-a53"], " ")) pctx.StaticVariable("ArmClangKraitCflags", strings.Join(armClangCpuVariantCflags["krait"], " ")) pctx.StaticVariable("ArmClangKryoCflags", @@ -255,8 +268,9 @@ var ( "cortex-a7": "${config.ArmCortexA7Cflags}", "cortex-a8": "${config.ArmCortexA8Cflags}", "cortex-a15": "${config.ArmCortexA15Cflags}", - "cortex-a53": "${config.ArmCortexA7Cflags}", - "cortex-a53.a57": "${config.ArmCortexA7Cflags}", + "cortex-a53": "${config.ArmCortexA53Cflags}", + "cortex-a53.a57": "${config.ArmCortexA53Cflags}", + "cortex-a73": "${config.ArmCortexA53Cflags}", "krait": "${config.ArmKraitCflags}", "kryo": "${config.ArmKryoCflags}", "denver": "${config.ArmCortexA15Cflags}", @@ -273,8 +287,9 @@ var ( "cortex-a7": "${config.ArmClangCortexA7Cflags}", "cortex-a8": "${config.ArmClangCortexA8Cflags}", "cortex-a15": "${config.ArmClangCortexA15Cflags}", - "cortex-a53": "${config.ArmClangCortexA7Cflags}", - "cortex-a53.a57": "${config.ArmClangCortexA7Cflags}", + "cortex-a53": "${config.ArmClangCortexA53Cflags}", + "cortex-a53.a57": "${config.ArmClangCortexA53Cflags}", + "cortex-a73": "${config.ArmClangCortexA53Cflags}", "krait": "${config.ArmClangKraitCflags}", "kryo": "${config.ArmClangKryoCflags}", "denver": "${config.ArmClangCortexA15Cflags}", diff --git a/cc/config/global.go b/cc/config/global.go index a29afec6..997256ef 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -104,7 +104,7 @@ func init() { // Everything in these lists is a crime against abstraction and dependency tracking. // Do not add anything to this list. - pctx.PrefixedPathsForOptionalSourceVariable("CommonGlobalIncludes", "-I", + pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", []string{ "system/core/include", "system/media/audio/include", @@ -118,7 +118,7 @@ func init() { }) // This is used by non-NDK modules to get jni.h. export_include_dirs doesn't help // with this, since there is no associated library. - pctx.PrefixedPathsForOptionalSourceVariable("CommonNativehelperInclude", "-I", + pctx.PrefixedExistentPathsForSourcesVariable("CommonNativehelperInclude", "-I", []string{"libnativehelper/include/nativehelper"}) pctx.SourcePathVariable("ClangDefaultBase", "prebuilts/clang/host") @@ -153,7 +153,7 @@ func init() { pctx.StaticVariable("RSLLVMPrebuiltsPath", "${RSClangBase}/${HostPrebuiltTag}/${RSClangVersion}/bin") pctx.StaticVariable("RSIncludePath", "${RSLLVMPrebuiltsPath}/../lib64/clang/${RSReleaseVersion}/include") - pctx.PrefixedPathsForOptionalSourceVariable("RsGlobalIncludes", "-I", + pctx.PrefixedExistentPathsForSourcesVariable("RsGlobalIncludes", "-I", []string{ "external/clang/lib/Headers", "frameworks/rs/script_api/include", diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go index b6b08fe1..65fa1edd 100644 --- a/cc/config/x86_darwin_host.go +++ b/cc/config/x86_darwin_host.go @@ -115,7 +115,8 @@ const ( func init() { pctx.VariableFunc("macSdkPath", func(config interface{}) (string, error) { - bytes, err := exec.Command("xcode-select", "--print-path").Output() + xcodeselect := config.(android.Config).HostSystemTool("xcode-select") + bytes, err := exec.Command(xcodeselect, "--print-path").Output() return strings.TrimSpace(string(bytes)), err }) pctx.VariableFunc("macSdkRoot", func(config interface{}) (string, error) { @@ -123,18 +124,16 @@ func init() { }) pctx.StaticVariable("macMinVersion", "10.8") pctx.VariableFunc("MacArPath", func(config interface{}) (string, error) { - bytes, err := exec.Command("xcrun", "--find", "ar").Output() - return strings.TrimSpace(string(bytes)), err + return xcrun(config.(android.Config), "--find", "ar") }) pctx.VariableFunc("MacStripPath", func(config interface{}) (string, error) { - bytes, err := exec.Command("xcrun", "--find", "strip").Output() - return strings.TrimSpace(string(bytes)), err + return xcrun(config.(android.Config), "--find", "strip") }) pctx.VariableFunc("MacToolPath", func(config interface{}) (string, error) { - bytes, err := exec.Command("xcrun", "--find", "ld").Output() - return filepath.Dir(strings.TrimSpace(string(bytes))), err + path, err := xcrun(config.(android.Config), "--find", "ld") + return filepath.Dir(path), err }) pctx.StaticVariable("DarwinGccVersion", darwinGccVersion) @@ -162,13 +161,20 @@ func init() { pctx.StaticVariable("DarwinX8664ClangLdflags", strings.Join(darwinX8664ClangLdflags, " ")) } +func xcrun(config android.Config, args ...string) (string, error) { + xcrun := config.HostSystemTool("xcrun") + bytes, err := exec.Command(xcrun, args...).Output() + return strings.TrimSpace(string(bytes)), err +} + func xcrunSdk(config android.Config, arg string) (string, error) { + xcrun := config.HostSystemTool("xcrun") if selected := config.Getenv("MAC_SDK_VERSION"); selected != "" { if !inList(selected, darwinSupportedSdkVersions) { return "", fmt.Errorf("MAC_SDK_VERSION %s isn't supported: %q", selected, darwinSupportedSdkVersions) } - bytes, err := exec.Command("xcrun", "--sdk", "macosx"+selected, arg).Output() + bytes, err := exec.Command(xcrun, "--sdk", "macosx"+selected, arg).Output() if err == nil { return strings.TrimSpace(string(bytes)), err } @@ -176,7 +182,7 @@ func xcrunSdk(config android.Config, arg string) (string, error) { } for _, sdk := range darwinSupportedSdkVersions { - bytes, err := exec.Command("xcrun", "--sdk", "macosx"+sdk, arg).Output() + bytes, err := exec.Command(xcrun, "--sdk", "macosx"+sdk, arg).Output() if err == nil { return strings.TrimSpace(string(bytes)), err } diff --git a/ui/build/Android.bp b/ui/build/Android.bp index d44c112d..7a83684c 100644 --- a/ui/build/Android.bp +++ b/ui/build/Android.bp @@ -25,6 +25,7 @@ bootstrap_go_package { "context.go", "environment.go", "exec.go", + "java.go", "kati.go", "make.go", "ninja.go", diff --git a/ui/build/build.go b/ui/build/build.go index d6059c0b..598e342d 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -15,8 +15,10 @@ package build import ( + "io/ioutil" "os" "path/filepath" + "strings" "text/template" ) @@ -59,6 +61,93 @@ const ( BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja ) +func checkCaseSensitivity(ctx Context, config Config) { + outDir := config.OutDir() + lowerCase := filepath.Join(outDir, "casecheck.txt") + upperCase := filepath.Join(outDir, "CaseCheck.txt") + lowerData := "a" + upperData := "B" + + err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777) + if err != nil { + ctx.Fatalln("Failed to check case sensitivity:", err) + } + + err = ioutil.WriteFile(upperCase, []byte(upperData), 0777) + if err != nil { + ctx.Fatalln("Failed to check case sensitivity:", err) + } + + res, err := ioutil.ReadFile(lowerCase) + if err != nil { + ctx.Fatalln("Failed to check case sensitivity:", err) + } + + if string(res) != lowerData { + ctx.Println("************************************************************") + ctx.Println("You are building on a case-insensitive filesystem.") + ctx.Println("Please move your source tree to a case-sensitive filesystem.") + ctx.Println("************************************************************") + ctx.Fatalln("Case-insensitive filesystems not supported") + } +} + +// Since products and build variants (unfortunately) shared the same +// PRODUCT_OUT staging directory, things can get out of sync if different +// build configurations are built in the same tree. This function will +// notice when the configuration has changed and call installclean to +// remove the files necessary to keep things consistent. +func installcleanIfNecessary(ctx Context, config Config) { + if inList("installclean", config.Arguments()) { + return + } + + configFile := config.DevicePreviousProductConfig() + prefix := "PREVIOUS_BUILD_CONFIG := " + suffix := "\n" + currentProduct := prefix + config.TargetProduct() + "-" + config.TargetBuildVariant() + suffix + + writeConfig := func() { + err := ioutil.WriteFile(configFile, []byte(currentProduct), 0777) + if err != nil { + ctx.Fatalln("Failed to write product config:", err) + } + } + + prev, err := ioutil.ReadFile(configFile) + if err != nil { + if os.IsNotExist(err) { + writeConfig() + return + } else { + ctx.Fatalln("Failed to read previous product config:", err) + } + } else if string(prev) == currentProduct { + return + } + + if disable, _ := config.Environment().Get("DISABLE_AUTO_INSTALLCLEAN"); disable == "true" { + ctx.Println("DISABLE_AUTO_INSTALLCLEAN is set; skipping auto-clean. Your tree may be in an inconsistent state.") + return + } + + ctx.BeginTrace("installclean") + defer ctx.EndTrace() + + prevConfig := strings.TrimPrefix(strings.TrimSuffix(string(prev), suffix), prefix) + currentConfig := strings.TrimPrefix(strings.TrimSuffix(currentProduct, suffix), prefix) + + ctx.Printf("Build configuration changed: %q -> %q, forcing installclean\n", prevConfig, currentConfig) + + cleanConfig := CopyConfig(ctx, config, "installclean") + cleanConfig.SetKatiArgs([]string{"installclean"}) + cleanConfig.SetNinjaArgs([]string{"installclean"}) + + Build(ctx, cleanConfig, BuildKati|BuildNinja) + + writeConfig() +} + // Build the tree. The 'what' argument can be used to chose which components of // the build to run. func Build(ctx Context, config Config, what int) { @@ -73,10 +162,26 @@ func Build(ctx Context, config Config, what int) { cmd.Stderr = ctx.Stderr() cmd.RunOrFatal() return + } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) { + // We can't use os.RemoveAll, since we don't want to remove the + // output directory itself, in case it's a symlink. So just do + // exactly what make used to do. + cmd := Command(ctx, config, "rm -rf $OUT_DIR/*", + "/bin/bash", "-c", "rm -rf "+config.OutDir()+"/*") + cmd.Stdout = ctx.Stdout() + cmd.Stderr = ctx.Stderr() + cmd.RunOrFatal() + ctx.Println("Entire build directory removed.") + return } + // Start getting java version as early as possible + getJavaVersions(ctx, config) + SetupOutDir(ctx, config) + checkCaseSensitivity(ctx, config) + if what&BuildProductConfig != 0 { // Run make for product config runMakeProductConfig(ctx, config) @@ -88,12 +193,17 @@ func Build(ctx Context, config Config, what int) { runSoong(ctx, config) } + // Check the java versions we read earlier + checkJavaVersion(ctx, config) + if what&BuildKati != 0 { // Run ckati runKati(ctx, config) } if what&BuildNinja != 0 { + installcleanIfNecessary(ctx, config) + // Write combined ninja file createCombinedBuildNinjaFile(ctx, config) diff --git a/ui/build/config.go b/ui/build/config.go index e677d930..51cff506 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -38,9 +38,10 @@ type configImpl struct { dist bool // From the product config - katiArgs []string - ninjaArgs []string - katiSuffix string + katiArgs []string + ninjaArgs []string + katiSuffix string + targetDevice string } const srcDirFileCheck = "build/soong/root.bp" @@ -106,6 +107,32 @@ func NewConfig(ctx Context, args ...string) Config { log.Fatalln("Error verifying tree state:", err) } + if srcDir, err := filepath.Abs("."); err == nil { + if strings.ContainsRune(srcDir, ' ') { + log.Println("You are building in a directory whose absolute path contains a space character:") + log.Println() + log.Printf("%q\n", srcDir) + log.Println() + log.Fatalln("Directory names containing spaces are not supported") + } + } + + if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') { + log.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:") + log.Println() + log.Printf("%q\n", outDir) + log.Println() + log.Fatalln("Directory names containing spaces are not supported") + } + + if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') { + log.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:") + log.Println() + log.Printf("%q\n", distDir) + log.Println() + log.Fatalln("Directory names containing spaces are not supported") + } + for _, arg := range args { arg = strings.TrimSpace(arg) if arg == "--make-mode" { @@ -140,6 +167,22 @@ func NewConfig(ctx Context, args ...string) Config { return Config{ret} } +// CopyConfig copies the configuration from an existing configuration, but replaces +// the Arguments() list with a new set. Useful if you need to run a different build +// with the same state as an existing build config. +func CopyConfig(ctx Context, config Config, args ...string) Config { + return Config{&configImpl{ + arguments: args, + goma: config.goma, + environ: config.environ.Copy(), + + parallel: config.parallel, + keepGoing: config.keepGoing, + verbose: config.verbose, + dist: config.dist, + }} +} + // Lunch configures the environment for a specific product similarly to the // `lunch` bash function. func (c *configImpl) Lunch(ctx Context, product, variant string) { @@ -245,6 +288,21 @@ func (c *configImpl) TargetProduct() string { panic("TARGET_PRODUCT is not defined") } +func (c *configImpl) TargetDevice() string { + return c.targetDevice +} + +func (c *configImpl) SetTargetDevice(device string) { + c.targetDevice = device +} + +func (c *configImpl) TargetBuildVariant() string { + if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok { + return v + } + panic("TARGET_BUILD_VARIANT is not defined") +} + func (c *configImpl) KatiArgs() []string { return c.katiArgs } @@ -311,6 +369,10 @@ func (c *configImpl) SoongMakeVarsMk() string { return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk") } +func (c *configImpl) DevicePreviousProductConfig() string { + return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice(), "previous_build_config.mk") +} + func (c *configImpl) HostPrebuiltTag() string { if runtime.GOOS == "linux" { return "linux-x86" diff --git a/ui/build/exec.go b/ui/build/exec.go index 4c45c507..c8c5c9a5 100644 --- a/ui/build/exec.go +++ b/ui/build/exec.go @@ -84,24 +84,38 @@ func (c *Cmd) StartOrFatal() { } } +func (c *Cmd) reportError(err error) { + if err == nil { + return + } + if e, ok := err.(*exec.ExitError); ok { + c.ctx.Fatalf("%s failed with: %v", c.name, e.ProcessState.String()) + } else { + c.ctx.Fatalf("Failed to run %s: %v", c.name, err) + } +} + // RunOrFatal is equivalent to Run, but handles the error with a call to ctx.Fatal func (c *Cmd) RunOrFatal() { - if err := c.Run(); err != nil { - if e, ok := err.(*exec.ExitError); ok { - c.ctx.Fatalf("%s failed with: %v", c.name, e.ProcessState.String()) - } else { - c.ctx.Fatalf("Failed to run %s: %v", c.name, err) - } - } + c.reportError(c.Run()) } // WaitOrFatal is equivalent to Wait, but handles the error with a call to ctx.Fatal func (c *Cmd) WaitOrFatal() { - if err := c.Wait(); err != nil { - if e, ok := err.(*exec.ExitError); ok { - c.ctx.Fatalf("%s failed with: %v", c.name, e.ProcessState.String()) - } else { - c.ctx.Fatalf("Failed to run %s: %v", c.name, err) - } - } + c.reportError(c.Wait()) +} + +// OutputOrFatal is equivalent to Output, but handles the error with a call to ctx.Fatal +func (c *Cmd) OutputOrFatal() []byte { + ret, err := c.Output() + c.reportError(err) + return ret +} + +// CombinedOutputOrFatal is equivalent to CombinedOutput, but handles the error with +// a call to ctx.Fatal +func (c *Cmd) CombinedOutputOrFatal() []byte { + ret, err := c.CombinedOutput() + c.reportError(err) + return ret } diff --git a/ui/build/java.go b/ui/build/java.go new file mode 100644 index 00000000..5a09b1a2 --- /dev/null +++ b/ui/build/java.go @@ -0,0 +1,156 @@ +// Copyright 2017 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 build + +import ( + "regexp" + "runtime" + "strings" + "sync" +) + +const incompatibleJavacStr = "google" + +var javaVersionInfo = struct { + once sync.Once + startOnce sync.Once + + java_version_output string + javac_version_output string +}{} + +func getJavaVersions(ctx Context, config Config) { + javaVersionInfo.startOnce.Do(func() { + go func() { + if ctx.Tracer != nil { + thread := ctx.Tracer.NewThread("java_version") + ctx.Tracer.Begin("get version", thread) + defer ctx.Tracer.End(thread) + } + + getJavaVersionsImpl(ctx, config) + }() + }) +} + +func getJavaVersionsImpl(ctx Context, config Config) { + javaVersionInfo.once.Do(func() { + cmd := Command(ctx, config, "java", "java", "-version") + cmd.Environment.Unset("_JAVA_OPTIONS") + javaVersionInfo.java_version_output = string(cmd.CombinedOutputOrFatal()) + + cmd = Command(ctx, config, "javac", "javac", "-version") + cmd.Environment.Unset("_JAVA_OPTIONS") + javaVersionInfo.javac_version_output = string(cmd.CombinedOutputOrFatal()) + }) +} + +func checkJavaVersion(ctx Context, config Config) { + ctx.BeginTrace("java_version_check") + defer ctx.EndTrace() + + getJavaVersionsImpl(ctx, config) + + var required_java_version string + var java_version_regexp *regexp.Regexp + var javac_version_regexp *regexp.Regexp + if legacy, _ := config.Environment().Get("LEGACY_USE_JAVA7"); legacy != "" { + required_java_version = "1.7" + java_version_regexp = regexp.MustCompile(`^java .*[ "]1\.7[\. "$]`) + javac_version_regexp = regexp.MustCompile(`[ "]1\.7[\. "$]`) + } else { + required_java_version = "1.8" + java_version_regexp = regexp.MustCompile(`[ "]1\.8[\. "$]`) + javac_version_regexp = java_version_regexp + } + + java_version := javaVersionInfo.java_version_output + javac_version := javaVersionInfo.javac_version_output + + found := false + for _, l := range strings.Split(java_version, "\n") { + if java_version_regexp.MatchString(l) { + java_version = l + found = true + break + } + } + if !found { + ctx.Println("***************************************************************") + ctx.Println("You are attempting to build with the incorrect version of java.") + ctx.Println() + ctx.Println("Your version is:", java_version) + ctx.Println("The required version is:", required_java_version+".x") + ctx.Println() + ctx.Println("Please follow the machine setup instructions at:") + ctx.Println(" https://source.android.com/source/initializing.html") + ctx.Println("***************************************************************") + ctx.Fatalln("stop") + } + + if runtime.GOOS == "linux" { + if !strings.Contains(java_version, "openjdk") { + ctx.Println("*******************************************************") + ctx.Println("You are attempting to build with an unsupported JDK.") + ctx.Println() + ctx.Println("Only an OpenJDK based JDK is supported.") + ctx.Println() + ctx.Println("Please follow the machine setup instructions at:") + ctx.Println(" https://source.android.com/source/initializing.html") + ctx.Println("*******************************************************") + ctx.Fatalln("stop") + } + } else { // darwin + if strings.Contains(java_version, "openjdk") { + ctx.Println("*******************************************************") + ctx.Println("You are attempting to build with an unsupported JDK.") + ctx.Println() + ctx.Println("You use OpenJDK, but only Sun/Oracle JDK is supported.") + ctx.Println() + ctx.Println("Please follow the machine setup instructions at:") + ctx.Println(" https://source.android.com/source/initializing.html") + ctx.Println("*******************************************************") + ctx.Fatalln("stop") + } + } + + incompatible_javac := strings.Contains(javac_version, incompatibleJavacStr) + + found = false + for _, l := range strings.Split(javac_version, "\n") { + if javac_version_regexp.MatchString(l) { + javac_version = l + found = true + break + } + } + if !found || incompatible_javac { + ctx.Println("****************************************************************") + ctx.Println("You are attempting to build with the incorrect version of javac.") + ctx.Println() + ctx.Println("Your version is:", javac_version) + if incompatible_javac { + ctx.Println("The '" + incompatibleJavacStr + "' version is not supported for Android platform builds.") + ctx.Println("Use a publically available JDK and make sure you have run envsetup.sh / lunch.") + } else { + ctx.Println("The required version is:", required_java_version) + } + ctx.Println() + ctx.Println("Please follow the machine setup instructions at:") + ctx.Println(" https://source.android.com/source/initializing.html") + ctx.Println("****************************************************************") + ctx.Fatalln("stop") + } +} diff --git a/ui/build/make.go b/ui/build/make.go index 32dc17b5..2b399263 100644 --- a/ui/build/make.go +++ b/ui/build/make.go @@ -83,6 +83,7 @@ func runMakeProductConfig(ctx Context, config Config) { // So that we can use the correct TARGET_PRODUCT if it's been // modified by PRODUCT-* arguments "TARGET_PRODUCT", + "TARGET_BUILD_VARIANT", // compiler wrappers set up by make "CC_WRAPPER", @@ -129,6 +130,9 @@ func runMakeProductConfig(ctx Context, config Config) { // Used to execute Kati and Ninja "NINJA_GOALS", "KATI_GOALS", + + // To find target/product/<DEVICE> + "TARGET_DEVICE", }, exportEnvVars...), bannerVars...) make_vars, err := DumpMakeVars(ctx, config, config.Arguments(), []string{ @@ -159,4 +163,5 @@ func runMakeProductConfig(ctx Context, config Config) { config.SetKatiArgs(strings.Fields(make_vars["KATI_GOALS"])) config.SetNinjaArgs(strings.Fields(make_vars["NINJA_GOALS"])) + config.SetTargetDevice(make_vars["TARGET_DEVICE"]) } diff --git a/ui/tracer/tracer.go b/ui/tracer/tracer.go index b3728856..f19ac186 100644 --- a/ui/tracer/tracer.go +++ b/ui/tracer/tracer.go @@ -46,6 +46,8 @@ type Tracer interface { Complete(name string, thread Thread, begin, end uint64) ImportNinjaLog(thread Thread, filename string, startOffset time.Time) + + NewThread(name string) Thread } type tracerImpl struct { |