Merge "droiddoc accepts aidl files as inputs"
diff --git a/android/config.go b/android/config.go
index 7f2a3ac..bc3d4f5 100644
--- a/android/config.go
+++ b/android/config.go
@@ -811,12 +811,12 @@
return c.config.productVariables.BoardOdmSepolicyDirs
}
-func (c *deviceConfig) PlatPublicSepolicyDir() string {
- return c.config.productVariables.BoardPlatPublicSepolicyDir
+func (c *deviceConfig) PlatPublicSepolicyDirs() []string {
+ return c.config.productVariables.BoardPlatPublicSepolicyDirs
}
-func (c *deviceConfig) PlatPrivateSepolicyDir() string {
- return c.config.productVariables.BoardPlatPrivateSepolicyDir
+func (c *deviceConfig) PlatPrivateSepolicyDirs() []string {
+ return c.config.productVariables.BoardPlatPrivateSepolicyDirs
}
func (c *config) IntegerOverflowDisabledForPath(path string) bool {
diff --git a/android/variable.go b/android/variable.go
index 6599ca3..af414cb 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -210,10 +210,10 @@
PgoAdditionalProfileDirs []string `json:",omitempty"`
- BoardVendorSepolicyDirs []string `json:",omitempty"`
- BoardOdmSepolicyDirs []string `json:",omitempty"`
- BoardPlatPublicSepolicyDir string `json:",omitempty"`
- BoardPlatPrivateSepolicyDir string `json:",omitempty"`
+ BoardVendorSepolicyDirs []string `json:",omitempty"`
+ BoardOdmSepolicyDirs []string `json:",omitempty"`
+ BoardPlatPublicSepolicyDirs []string `json:",omitempty"`
+ BoardPlatPrivateSepolicyDirs []string `json:",omitempty"`
VendorVars map[string]map[string]string `json:",omitempty"`
}
diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go
index 37877c8..ded9efa 100644
--- a/androidmk/cmd/androidmk/android.go
+++ b/androidmk/cmd/androidmk/android.go
@@ -147,7 +147,7 @@
"LOCAL_ANNOTATION_PROCESSOR_CLASSES": "annotation_processor_classes",
"LOCAL_PROGUARD_FLAGS": "optimize.proguard_flags",
- "LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flag_files",
+ "LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flags_files",
// These will be rewritten to libs/static_libs by bpfix, after their presence is used to convert
// java_library_static to android_library.
@@ -179,6 +179,8 @@
"LOCAL_DEX_PREOPT": "dex_preopt.enabled",
"LOCAL_DEX_PREOPT_APP_IMAGE": "dex_preopt.app_image",
"LOCAL_DEX_PREOPT_GENERATE_PROFILE": "dex_preopt.profile_guided",
+
+ "LOCAL_PRIVATE_PLATFORM_APIS": "platform_apis",
})
}
diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go
index a54a4d2..ed5ae02 100644
--- a/androidmk/cmd/androidmk/androidmk_test.go
+++ b/androidmk/cmd/androidmk/androidmk_test.go
@@ -378,6 +378,33 @@
}
`,
},
+ {
+ desc: "Convert LOCAL_MODULE_TAGS tests to java_test",
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_HOST_JAVA_LIBRARY)
+`,
+
+ expected: `
+java_test {
+}
+
+android_test {
+}
+
+java_test_host {
+}
+`,
+ },
{
desc: "Input containing escaped quotes",
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index ee00907..05be3bd 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -45,12 +45,43 @@
// A FixRequest specifies the details of which fixes to apply to an individual file
// A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go
type FixRequest struct {
- simplifyKnownRedundantVariables bool
- rewriteIncorrectAndroidmkPrebuilts bool
- rewriteIncorrectAndroidmkAndroidLibraries bool
- mergeMatchingModuleProperties bool
- reorderCommonProperties bool
- removeTags bool
+ steps []fixStep
+}
+
+type fixStep struct {
+ name string
+ fix func(f *Fixer) error
+}
+
+var fixSteps = []fixStep{
+ {
+ name: "simplifyKnownRedundantVariables",
+ fix: runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther),
+ },
+ {
+ name: "rewriteIncorrectAndroidmkPrebuilts",
+ fix: rewriteIncorrectAndroidmkPrebuilts,
+ },
+ {
+ name: "rewriteIncorrectAndroidmkAndroidLibraries",
+ fix: rewriteIncorrectAndroidmkAndroidLibraries,
+ },
+ {
+ name: "rewriteTestModuleTypes",
+ fix: rewriteTestModuleTypes,
+ },
+ {
+ name: "mergeMatchingModuleProperties",
+ fix: runPatchListMod(mergeMatchingModuleProperties),
+ },
+ {
+ name: "reorderCommonProperties",
+ fix: runPatchListMod(reorderCommonProperties),
+ },
+ {
+ name: "removeTags",
+ fix: runPatchListMod(removeTags),
+ },
}
func NewFixRequest() FixRequest {
@@ -58,13 +89,8 @@
}
func (r FixRequest) AddAll() (result FixRequest) {
- result = r
- result.simplifyKnownRedundantVariables = true
- result.rewriteIncorrectAndroidmkPrebuilts = true
- result.rewriteIncorrectAndroidmkAndroidLibraries = true
- result.mergeMatchingModuleProperties = true
- result.reorderCommonProperties = true
- result.removeTags = true
+ result.steps = append([]fixStep(nil), r.steps...)
+ result.steps = append(result.steps, fixSteps...)
return result
}
@@ -148,43 +174,8 @@
}
func (f *Fixer) fixTreeOnce(config FixRequest) error {
- if config.simplifyKnownRedundantVariables {
- err := f.runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)
- if err != nil {
- return err
- }
- }
-
- if config.rewriteIncorrectAndroidmkPrebuilts {
- err := f.rewriteIncorrectAndroidmkPrebuilts()
- if err != nil {
- return err
- }
- }
-
- if config.rewriteIncorrectAndroidmkAndroidLibraries {
- err := f.rewriteIncorrectAndroidmkAndroidLibraries()
- if err != nil {
- return err
- }
- }
-
- if config.mergeMatchingModuleProperties {
- err := f.runPatchListMod(mergeMatchingModuleProperties)
- if err != nil {
- return err
- }
- }
-
- if config.reorderCommonProperties {
- err := f.runPatchListMod(reorderCommonProperties)
- if err != nil {
- return err
- }
- }
-
- if config.removeTags {
- err := f.runPatchListMod(removeTags)
+ for _, fix := range config.steps {
+ err := fix.fix(f)
if err != nil {
return err
}
@@ -198,7 +189,7 @@
"export_include_dirs", "local_include_dirs")
}
-func (f *Fixer) rewriteIncorrectAndroidmkPrebuilts() error {
+func rewriteIncorrectAndroidmkPrebuilts(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
if !ok {
@@ -234,7 +225,7 @@
return nil
}
-func (f *Fixer) rewriteIncorrectAndroidmkAndroidLibraries() error {
+func rewriteIncorrectAndroidmkAndroidLibraries(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
if !ok {
@@ -269,45 +260,87 @@
return nil
}
-func (f *Fixer) runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) error {
- // Make sure all the offsets are accurate
- buf, err := f.reparse()
- if err != nil {
- return err
- }
-
- var patchlist parser.PatchList
+// rewriteTestModuleTypes looks for modules that are identifiable as tests but for which Make doesn't have a separate
+// module class, and moves them to the appropriate Soong module type.
+func rewriteTestModuleTypes(f *Fixer) error {
for _, def := range f.tree.Defs {
mod, ok := def.(*parser.Module)
if !ok {
continue
}
- err := modFunc(mod, buf, &patchlist)
- if err != nil {
- return err
+ if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") {
+ continue
+ }
+
+ hasInstrumentationFor := hasNonEmptyLiteralStringProperty(mod, "instrumentation_for")
+ tags, _ := getLiteralListPropertyValue(mod, "tags")
+
+ var hasTestsTag bool
+ for _, tag := range tags {
+ if tag == "tests" {
+ hasTestsTag = true
+ }
+ }
+
+ isTest := hasInstrumentationFor || hasTestsTag
+
+ if isTest {
+ switch mod.Type {
+ case "android_app":
+ mod.Type = "android_test"
+ case "java_library":
+ mod.Type = "java_test"
+ case "java_library_host":
+ mod.Type = "java_test_host"
+ }
}
}
- newBuf := new(bytes.Buffer)
- err = patchlist.Apply(bytes.NewReader(buf), newBuf)
- if err != nil {
- return err
- }
-
- // Save a copy of the buffer to print for errors below
- bufCopy := append([]byte(nil), newBuf.Bytes()...)
-
- newTree, err := parse(f.tree.Name, newBuf)
- if err != nil {
- return fmt.Errorf("Failed to parse: %v\nBuffer:\n%s", err, string(bufCopy))
- }
-
- f.tree = newTree
-
return nil
}
+func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error {
+ return func(f *Fixer) error {
+ // Make sure all the offsets are accurate
+ buf, err := f.reparse()
+ if err != nil {
+ return err
+ }
+
+ var patchlist parser.PatchList
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+
+ err := modFunc(mod, buf, &patchlist)
+ if err != nil {
+ return err
+ }
+ }
+
+ newBuf := new(bytes.Buffer)
+ err = patchlist.Apply(bytes.NewReader(buf), newBuf)
+ if err != nil {
+ return err
+ }
+
+ // Save a copy of the buffer to print for errors below
+ bufCopy := append([]byte(nil), newBuf.Bytes()...)
+
+ newTree, err := parse(f.tree.Name, newBuf)
+ if err != nil {
+ return fmt.Errorf("Failed to parse: %v\nBuffer:\n%s", err, string(bufCopy))
+ }
+
+ f.tree = newTree
+
+ return nil
+ }
+}
+
var commonPropertyPriorities = []string{
"name",
"defaults",
@@ -394,26 +427,34 @@
// force installation for -eng builds.
`
case "tests":
- if strings.Contains(mod.Type, "cc_test") || strings.Contains(mod.Type, "cc_library_static") {
+ switch {
+ case strings.Contains(mod.Type, "cc_test"),
+ strings.Contains(mod.Type, "cc_library_static"),
+ strings.Contains(mod.Type, "java_test"),
+ mod.Type == "android_test":
continue
- } else if strings.Contains(mod.Type, "cc_lib") {
+ case strings.Contains(mod.Type, "cc_lib"):
replaceStr += `// WARNING: Module tags are not supported in Soong.
// To make a shared library only for tests, use the "cc_test_library" module
// type. If you don't use gtest, set "gtest: false".
`
- } else if strings.Contains(mod.Type, "cc_bin") {
+ case strings.Contains(mod.Type, "cc_bin"):
replaceStr += `// WARNING: Module tags are not supported in Soong.
// For native test binaries, use the "cc_test" module type. Some differences:
// - If you don't use gtest, set "gtest: false"
// - Binaries will be installed into /data/nativetest[64]/<name>/<name>
// - Both 32 & 64 bit versions will be built (as appropriate)
`
- } else if strings.Contains(mod.Type, "java_lib") {
+ case strings.Contains(mod.Type, "java_lib"):
replaceStr += `// WARNING: Module tags are not supported in Soong.
// For JUnit or similar tests, use the "java_test" module type. A dependency on
// Junit will be added by default, if it is using some other runner, set "junit: false".
`
- } else {
+ case mod.Type == "android_app":
+ replaceStr += `// WARNING: Module tags are not supported in Soong.
+ // For JUnit or instrumentataion app tests, use the "android_test" module type.
+ `
+ default:
replaceStr += `// WARNING: Module tags are not supported in Soong.
// In most cases, tests are now identified by their module type:
// cc_test, java_test, python_test
@@ -563,6 +604,11 @@
return found && len(list.Values) > 0
}
+func hasNonEmptyLiteralStringProperty(mod *parser.Module, name string) bool {
+ s, found := getLiteralStringPropertyValue(mod, name)
+ return found && len(s) > 0
+}
+
func getLiteralListProperty(mod *parser.Module, name string) (list *parser.List, found bool) {
prop, ok := mod.GetProperty(name)
if !ok {
@@ -572,6 +618,40 @@
return list, ok
}
+func getLiteralListPropertyValue(mod *parser.Module, name string) (list []string, found bool) {
+ listValue, ok := getLiteralListProperty(mod, name)
+ if !ok {
+ return nil, false
+ }
+ for _, v := range listValue.Values {
+ stringValue, ok := v.(*parser.String)
+ if !ok {
+ return nil, false
+ }
+ list = append(list, stringValue.Value)
+ }
+
+ return list, true
+}
+
+func getLiteralStringProperty(mod *parser.Module, name string) (s *parser.String, found bool) {
+ prop, ok := mod.GetProperty(name)
+ if !ok {
+ return nil, false
+ }
+ s, ok = prop.Value.(*parser.String)
+ return s, ok
+}
+
+func getLiteralStringPropertyValue(mod *parser.Module, name string) (s string, found bool) {
+ stringValue, ok := getLiteralStringProperty(mod, name)
+ if !ok {
+ return "", false
+ }
+
+ return stringValue.Value, true
+}
+
func propertyIndex(props []*parser.Property, propertyName string) int {
for i, prop := range props {
if prop.Name == propertyName {
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 654ccf9..6ba93f6 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -66,7 +66,7 @@
fixer := NewFixer(tree)
// apply simplifications
- err := fixer.runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)
+ err := runPatchListMod(simplifyKnownPropertiesDuplicatingEachOther)(fixer)
if len(errs) > 0 {
t.Fatal(err)
}
@@ -251,7 +251,7 @@
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
runPass(t, test.in, test.out, func(fixer *Fixer) error {
- return fixer.runPatchListMod(mergeMatchingModuleProperties)
+ return runPatchListMod(mergeMatchingModuleProperties)(fixer)
})
})
}
@@ -337,7 +337,7 @@
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
runPass(t, test.in, test.out, func(fixer *Fixer) error {
- return fixer.runPatchListMod(reorderCommonProperties)
+ return runPatchListMod(reorderCommonProperties)(fixer)
})
})
}
@@ -490,9 +490,9 @@
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
runPass(t, test.in, test.out, func(fixer *Fixer) error {
- return fixer.runPatchListMod(func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+ return runPatchListMod(func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
return removeMatchingModuleListProperties(mod, patchList, "bar", "foo")
- })
+ })(fixer)
})
})
}
diff --git a/cc/cc.go b/cc/cc.go
index a885c91..e261d40 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1554,16 +1554,35 @@
}
if genrule, ok := mctx.Module().(*genrule.Module); ok {
- if props, ok := genrule.Extra.(*VendorProperties); ok {
+ if props, ok := genrule.Extra.(*GenruleExtraProperties); ok {
+ var coreVariantNeeded bool = false
+ var vendorVariantNeeded bool = false
+ var recoveryVariantNeeded bool = false
if mctx.DeviceConfig().VndkVersion() == "" {
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
} else if Bool(props.Vendor_available) {
- mctx.CreateVariations(coreMode, vendorMode)
+ coreVariantNeeded = true
+ vendorVariantNeeded = true
} else if mctx.SocSpecific() || mctx.DeviceSpecific() {
- mctx.CreateVariations(vendorMode)
+ vendorVariantNeeded = true
} else {
- mctx.CreateVariations(coreMode)
+ coreVariantNeeded = true
}
+ if Bool(props.Recovery_available) {
+ recoveryVariantNeeded = true
+ }
+
+ var variants []string
+ if coreVariantNeeded {
+ variants = append(variants, coreMode)
+ }
+ if vendorVariantNeeded {
+ variants = append(variants, vendorMode)
+ }
+ if recoveryVariantNeeded {
+ variants = append(variants, recoveryMode)
+ }
+ mctx.CreateVariations(variants...)
}
}
diff --git a/cc/compiler.go b/cc/compiler.go
index b410115..8f119cf 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -258,6 +258,8 @@
CheckBadCompilerFlags(ctx, "cppflags", compiler.Properties.Cppflags)
CheckBadCompilerFlags(ctx, "conlyflags", compiler.Properties.Conlyflags)
CheckBadCompilerFlags(ctx, "asflags", compiler.Properties.Asflags)
+ CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags)
+ CheckBadCompilerFlags(ctx, "recovery.cflags", compiler.Properties.Target.Recovery.Cflags)
esc := proptools.NinjaAndShellEscape
@@ -454,6 +456,10 @@
flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
}
+ if ctx.inRecovery() {
+ flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Recovery.Cflags)...)
+ }
+
// We can enforce some rules more strictly in the code we own. strict
// indicates if this is code that we can be stricter with. If we have
// rules that we want to apply to *our* code (but maybe can't for
diff --git a/cc/gen.go b/cc/gen.go
index f22a783..c794f5c 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -25,7 +25,7 @@
)
func init() {
- pctx.SourcePathVariable("lexCmd", "prebuilts/misc/${config.HostPrebuiltTag}/flex/flex-2.5.39")
+ pctx.SourcePathVariable("lexCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/flex")
pctx.SourcePathVariable("yaccCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/bison")
pctx.SourcePathVariable("yaccDataDir", "prebuilts/build-tools/common/bison")
diff --git a/cc/genrule.go b/cc/genrule.go
index 51c0d16..a672992 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -23,13 +23,18 @@
android.RegisterModuleType("cc_genrule", genRuleFactory)
}
+type GenruleExtraProperties struct {
+ Vendor_available *bool
+ Recovery_available *bool
+}
+
// cc_genrule is a genrule that can depend on other cc_* objects.
// The cmd may be run multiple times, once for each of the different arch/etc
// variations.
func genRuleFactory() android.Module {
module := genrule.NewGenRule()
- module.Extra = &VendorProperties{}
+ module.Extra = &GenruleExtraProperties{}
module.AddProperties(module.Extra)
android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibBoth)
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index fd563e6..825b920 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libbuildversion",
host_supported: true,
+ recovery_available: true,
srcs: ["libbuildversion.cpp"],
export_include_dirs: ["include"],
cflags: ["-fvisibility=hidden"],
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 9fabc97..0ee0f05 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -26,7 +26,7 @@
)
var (
- preprocessBionicHeaders = pctx.AndroidStaticRule("preprocessBionicHeaders",
+ versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders",
blueprint.RuleParams{
// The `&& touch $out` isn't really necessary, but Blueprint won't
// let us have only implicit outputs.
@@ -45,7 +45,7 @@
return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
}
-type headerProperies struct {
+type headerProperties struct {
// Base directory of the headers being installed. As an example:
//
// ndk_headers {
@@ -65,6 +65,9 @@
// List of headers to install. Glob compatible. Common case is "include/**/*.h".
Srcs []string
+ // Source paths that should be excluded from the srcs glob.
+ Exclude_srcs []string
+
// Path to the NOTICE file associated with the headers.
License *string
}
@@ -72,7 +75,7 @@
type headerModule struct {
android.ModuleBase
- properties headerProperies
+ properties headerProperties
installPaths android.Paths
licensePath android.ModuleSrcPath
@@ -128,7 +131,7 @@
return
}
- srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
+ srcFiles := ctx.ExpandSources(m.properties.Srcs, m.properties.Exclude_srcs)
for _, header := range srcFiles {
installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
String(m.properties.To))
@@ -154,10 +157,10 @@
return module
}
-type preprocessedHeaderProperies struct {
+type versionedHeaderProperties struct {
// Base directory of the headers being installed. As an example:
//
- // preprocessed_ndk_headers {
+ // versioned_ndk_headers {
// name: "foo",
// from: "include",
// to: "",
@@ -181,19 +184,19 @@
// module does not have the srcs property, and operates on a full directory (the `from` property).
//
// Note that this is really only built to handle bionic/libc/include.
-type preprocessedHeaderModule struct {
+type versionedHeaderModule struct {
android.ModuleBase
- properties preprocessedHeaderProperies
+ properties versionedHeaderProperties
installPaths android.Paths
licensePath android.ModuleSrcPath
}
-func (m *preprocessedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (m *versionedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
}
-func (m *preprocessedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if String(m.properties.License) == "" {
ctx.PropertyErrorf("license", "field is required")
}
@@ -248,7 +251,7 @@
timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
ctx.Build(pctx, android.BuildParams{
- Rule: preprocessBionicHeaders,
+ Rule: versionBionicHeaders,
Description: "versioner preprocess " + srcDir.Rel(),
Output: timestampFile,
Implicits: append(srcFiles, depsGlob...),
@@ -263,8 +266,8 @@
return timestampFile
}
-func preprocessedNdkHeadersFactory() android.Module {
- module := &preprocessedHeaderModule{}
+func versionedNdkHeadersFactory() android.Module {
+ module := &versionedHeaderModule{}
module.AddProperties(&module.properties)
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index c7ba588..1a46702 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -59,7 +59,7 @@
func init() {
android.RegisterModuleType("ndk_headers", ndkHeadersFactory)
android.RegisterModuleType("ndk_library", ndkLibraryFactory)
- android.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
+ android.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
android.RegisterSingletonType("ndk", NdkSingleton)
pctx.Import("android/soong/common")
@@ -107,7 +107,7 @@
licensePaths = append(licensePaths, m.licensePath)
}
- if m, ok := module.(*preprocessedHeaderModule); ok {
+ if m, ok := module.(*versionedHeaderModule); ok {
installPaths = append(installPaths, m.installPaths...)
licensePaths = append(licensePaths, m.licensePath)
}
diff --git a/cmd/path_interposer/Android.bp b/cmd/path_interposer/Android.bp
new file mode 100644
index 0000000..41a219f
--- /dev/null
+++ b/cmd/path_interposer/Android.bp
@@ -0,0 +1,20 @@
+// Copyright 2018 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.
+
+blueprint_go_binary {
+ name: "path_interposer",
+ deps: ["soong-ui-build-paths"],
+ srcs: ["main.go"],
+ testSrcs: ["main_test.go"],
+}
diff --git a/cmd/path_interposer/main.go b/cmd/path_interposer/main.go
new file mode 100644
index 0000000..cd28b96
--- /dev/null
+++ b/cmd/path_interposer/main.go
@@ -0,0 +1,247 @@
+// Copyright 2018 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 main
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "syscall"
+
+ "android/soong/ui/build/paths"
+)
+
+func main() {
+ interposer, err := os.Executable()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "Unable to locate interposer executable:", err)
+ os.Exit(1)
+ }
+
+ if fi, err := os.Lstat(interposer); err == nil {
+ if fi.Mode()&os.ModeSymlink != 0 {
+ link, err := os.Readlink(interposer)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "Unable to read link to interposer executable:", err)
+ os.Exit(1)
+ }
+ if filepath.IsAbs(link) {
+ interposer = link
+ } else {
+ interposer = filepath.Join(filepath.Dir(interposer), link)
+ }
+ }
+ } else {
+ fmt.Fprintln(os.Stderr, "Unable to stat interposer executable:", err)
+ os.Exit(1)
+ }
+
+ disableError := false
+ if e, ok := os.LookupEnv("TEMPORARY_DISABLE_PATH_RESTRICTIONS"); ok {
+ disableError = e == "1" || e == "y" || e == "yes" || e == "on" || e == "true"
+ }
+
+ exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{
+ disableError: disableError,
+
+ sendLog: paths.SendLog,
+ config: paths.GetConfig,
+ lookupParents: lookupParents,
+ })
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err.Error())
+ }
+ os.Exit(exitCode)
+}
+
+var usage = fmt.Errorf(`To use the PATH interposer:
+ * Write the original PATH variable to <interposer>_origpath
+ * Set up a directory of symlinks to the PATH interposer, and use that in PATH
+
+If a tool isn't in the allowed list, a log will be posted to the unix domain
+socket at <interposer>_log.`)
+
+type mainOpts struct {
+ disableError bool
+
+ sendLog func(logSocket string, entry *paths.LogEntry, done chan interface{})
+ config func(name string) paths.PathConfig
+ lookupParents func() []paths.LogProcess
+}
+
+func Main(stdout, stderr io.Writer, interposer string, args []string, opts mainOpts) (int, error) {
+ base := filepath.Base(args[0])
+
+ origPathFile := interposer + "_origpath"
+ if base == filepath.Base(interposer) {
+ return 1, usage
+ }
+
+ origPath, err := ioutil.ReadFile(origPathFile)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return 1, usage
+ } else {
+ return 1, fmt.Errorf("Failed to read original PATH: %v", err)
+ }
+ }
+
+ cmd := &exec.Cmd{
+ Args: args,
+ Env: os.Environ(),
+
+ Stdin: os.Stdin,
+ Stdout: stdout,
+ Stderr: stderr,
+ }
+
+ if err := os.Setenv("PATH", string(origPath)); err != nil {
+ return 1, fmt.Errorf("Failed to set PATH env: %v", err)
+ }
+
+ if config := opts.config(base); config.Log || config.Error {
+ var procs []paths.LogProcess
+ if opts.lookupParents != nil {
+ procs = opts.lookupParents()
+ }
+
+ if opts.sendLog != nil {
+ waitForLog := make(chan interface{})
+ opts.sendLog(interposer+"_log", &paths.LogEntry{
+ Basename: base,
+ Args: args,
+ Parents: procs,
+ }, waitForLog)
+ defer func() { <-waitForLog }()
+ }
+ if config.Error && !opts.disableError {
+ return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.", base)
+ }
+ }
+
+ cmd.Path, err = exec.LookPath(base)
+ if err != nil {
+ return 1, err
+ }
+
+ if err = cmd.Run(); err != nil {
+ if exitErr, ok := err.(*exec.ExitError); ok {
+ if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
+ if status.Exited() {
+ return status.ExitStatus(), nil
+ } else if status.Signaled() {
+ exitCode := 128 + int(status.Signal())
+ return exitCode, nil
+ } else {
+ return 1, exitErr
+ }
+ } else {
+ return 1, nil
+ }
+ }
+ }
+
+ return 0, nil
+}
+
+type procEntry struct {
+ Pid int
+ Ppid int
+ Command string
+}
+
+func readProcs() map[int]procEntry {
+ cmd := exec.Command("ps", "-o", "pid,ppid,command")
+ data, err := cmd.Output()
+ if err != nil {
+ return nil
+ }
+
+ return parseProcs(data)
+}
+
+func parseProcs(data []byte) map[int]procEntry {
+ lines := bytes.Split(data, []byte("\n"))
+ if len(lines) < 2 {
+ return nil
+ }
+ // Remove the header
+ lines = lines[1:]
+
+ ret := make(map[int]procEntry, len(lines))
+ for _, line := range lines {
+ fields := bytes.SplitN(line, []byte(" "), 2)
+ if len(fields) != 2 {
+ continue
+ }
+
+ pid, err := strconv.Atoi(string(fields[0]))
+ if err != nil {
+ continue
+ }
+
+ line = bytes.TrimLeft(fields[1], " ")
+
+ fields = bytes.SplitN(line, []byte(" "), 2)
+ if len(fields) != 2 {
+ continue
+ }
+
+ ppid, err := strconv.Atoi(string(fields[0]))
+ if err != nil {
+ continue
+ }
+
+ ret[pid] = procEntry{
+ Pid: pid,
+ Ppid: ppid,
+ Command: string(bytes.TrimLeft(fields[1], " ")),
+ }
+ }
+
+ return ret
+}
+
+func lookupParents() []paths.LogProcess {
+ procs := readProcs()
+ if procs == nil {
+ return nil
+ }
+
+ list := []paths.LogProcess{}
+ pid := os.Getpid()
+ for {
+ entry, ok := procs[pid]
+ if !ok {
+ break
+ }
+
+ list = append([]paths.LogProcess{
+ {
+ Pid: pid,
+ Command: entry.Command,
+ },
+ }, list...)
+
+ pid = entry.Ppid
+ }
+
+ return list
+}
diff --git a/cmd/path_interposer/main_test.go b/cmd/path_interposer/main_test.go
new file mode 100644
index 0000000..4b25c44
--- /dev/null
+++ b/cmd/path_interposer/main_test.go
@@ -0,0 +1,196 @@
+// Copyright 2018 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 main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "android/soong/ui/build/paths"
+)
+
+var tmpDir string
+var origPATH string
+
+func TestMain(m *testing.M) {
+ os.Exit(func() int {
+ var err error
+ tmpDir, err = ioutil.TempDir("", "interposer_test")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ origPATH = os.Getenv("PATH")
+ err = os.Setenv("PATH", "")
+ if err != nil {
+ panic(err)
+ }
+
+ return m.Run()
+ }())
+}
+
+func setup(t *testing.T) string {
+ f, err := ioutil.TempFile(tmpDir, "interposer")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ err = ioutil.WriteFile(f.Name()+"_origpath", []byte(origPATH), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f.Name()
+}
+
+func TestInterposer(t *testing.T) {
+ interposer := setup(t)
+
+ logConfig := func(name string) paths.PathConfig {
+ if name == "true" {
+ return paths.PathConfig{
+ Log: false,
+ Error: false,
+ }
+ } else if name == "path_interposer_test_not_allowed" {
+ return paths.PathConfig{
+ Log: false,
+ Error: true,
+ }
+ }
+ return paths.PathConfig{
+ Log: true,
+ Error: false,
+ }
+ }
+
+ testCases := []struct {
+ name string
+ args []string
+
+ exitCode int
+ err error
+ logEntry string
+ }{
+ {
+ name: "direct call",
+ args: []string{interposer},
+
+ exitCode: 1,
+ err: usage,
+ },
+ {
+ name: "relative call",
+ args: []string{filepath.Base(interposer)},
+
+ exitCode: 1,
+ err: usage,
+ },
+ {
+ name: "true",
+ args: []string{"/my/path/true"},
+ },
+ {
+ name: "relative true",
+ args: []string{"true"},
+ },
+ {
+ name: "exit code",
+ args: []string{"bash", "-c", "exit 42"},
+
+ exitCode: 42,
+ logEntry: "bash",
+ },
+ {
+ name: "signal",
+ args: []string{"bash", "-c", "kill -9 $$"},
+
+ exitCode: 137,
+ logEntry: "bash",
+ },
+ {
+ name: "does not exist",
+ args: []string{"path_interposer_test_does_not_exist"},
+
+ exitCode: 1,
+ err: fmt.Errorf(`exec: "path_interposer_test_does_not_exist": executable file not found in $PATH`),
+ logEntry: "path_interposer_test_does_not_exist",
+ },
+ {
+ name: "not allowed",
+ args: []string{"path_interposer_test_not_allowed"},
+
+ exitCode: 1,
+ err: fmt.Errorf(`"path_interposer_test_not_allowed" is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.`),
+ logEntry: "path_interposer_test_not_allowed",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ logged := false
+ logFunc := func(logSocket string, entry *paths.LogEntry, done chan interface{}) {
+ defer close(done)
+
+ logged = true
+ if entry.Basename != testCase.logEntry {
+ t.Errorf("unexpected log entry:\nwant: %q\n got: %q", testCase.logEntry, entry.Basename)
+ }
+ }
+
+ exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, testCase.args, mainOpts{
+ sendLog: logFunc,
+ config: logConfig,
+ })
+
+ errstr := func(err error) string {
+ if err == nil {
+ return ""
+ }
+ return err.Error()
+ }
+ if errstr(testCase.err) != errstr(err) {
+ t.Errorf("unexpected error:\nwant: %v\n got: %v", testCase.err, err)
+ }
+ if testCase.exitCode != exitCode {
+ t.Errorf("expected exit code %d, got %d", testCase.exitCode, exitCode)
+ }
+ if !logged && testCase.logEntry != "" {
+ t.Errorf("no log entry, but expected %q", testCase.logEntry)
+ }
+ })
+ }
+}
+
+func TestMissingPath(t *testing.T) {
+ interposer := setup(t)
+ err := os.Remove(interposer + "_origpath")
+ if err != nil {
+ t.Fatalf("Failed to remove:", err)
+ }
+
+ exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, []string{"true"}, mainOpts{})
+ if err != usage {
+ t.Errorf("Unexpected error:\n got: %v\nwant: %v", err, usage)
+ }
+ if exitCode != 1 {
+ t.Errorf("expected exit code %d, got %d", 1, exitCode)
+ }
+}
diff --git a/java/aar.go b/java/aar.go
index 66f1cab..21e38a4 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -354,6 +354,7 @@
proguardFlags android.WritablePath
exportPackage android.WritablePath
extraAaptPackagesFile android.WritablePath
+ manifest android.WritablePath
exportedStaticPackages android.Paths
}
@@ -413,12 +414,12 @@
extractedResDir := extractedAARDir.Join(ctx, "res")
a.classpathFile = extractedAARDir.Join(ctx, "classes.jar")
a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
- manifest := extractedAARDir.Join(ctx, "AndroidManifest.xml")
+ a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
ctx.Build(pctx, android.BuildParams{
Rule: unzipAAR,
Input: aar,
- Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, manifest},
+ Outputs: android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
Description: "unzip AAR",
Args: map[string]string{
"expectedDirs": extractedResDir.String(),
@@ -446,8 +447,8 @@
"--auto-add-overlay",
}
- linkFlags = append(linkFlags, "--manifest "+manifest.String())
- linkDeps = append(linkDeps, manifest)
+ linkFlags = append(linkFlags, "--manifest "+a.manifest.String())
+ linkDeps = append(linkDeps, a.manifest)
transitiveStaticLibs, libDeps, libFlags := aaptLibs(ctx, String(a.properties.Sdk_version))
diff --git a/java/androidmk.go b/java/androidmk.go
index 88dc6ef..0840198 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -136,6 +136,7 @@
fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", prebuilt.exportPackage.String())
fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", prebuilt.proguardFlags.String())
fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", prebuilt.extraAaptPackagesFile.String())
+ fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", prebuilt.manifest.String())
fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(prebuilt.properties.Sdk_version))
},
},
@@ -228,6 +229,19 @@
}
}
+func (a *AndroidTest) AndroidMk() android.AndroidMkData {
+ data := a.AndroidApp.AndroidMk()
+ data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_MODULE_TAGS := tests")
+ if len(a.testProperties.Test_suites) > 0 {
+ fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE :=",
+ strings.Join(a.testProperties.Test_suites, " "))
+ }
+ })
+
+ return data
+}
+
func (a *AndroidLibrary) AndroidMk() android.AndroidMkData {
data := a.Library.AndroidMk()
diff --git a/java/app.go b/java/app.go
index ae0592a..37109b5 100644
--- a/java/app.go
+++ b/java/app.go
@@ -26,6 +26,7 @@
func init() {
android.RegisterModuleType("android_app", AndroidAppFactory)
+ android.RegisterModuleType("android_test", AndroidTestFactory)
}
// AndroidManifest.xml merging
@@ -50,8 +51,6 @@
// list of resource labels to generate individual resource packages
Package_splits []string
-
- Instrumentation_for *string
}
type AndroidApp struct {
@@ -61,6 +60,8 @@
certificate certificate
appProperties appProperties
+
+ extraLinkFlags []string
}
func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths {
@@ -85,14 +86,11 @@
}
func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- var linkFlags []string
- if String(a.appProperties.Instrumentation_for) != "" {
- linkFlags = append(linkFlags,
- "--rename-instrumentation-target-package",
- String(a.appProperties.Instrumentation_for))
- } else {
- a.properties.Instrument = true
- }
+ a.generateAndroidBuildActions(ctx)
+}
+
+func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
+ linkFlags := append([]string(nil), a.extraLinkFlags...)
hasProduct := false
for _, f := range a.aaptProperties.Aaptflags {
@@ -188,6 +186,8 @@
module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
+ module.Module.properties.Instrument = true
+
module.AddProperties(
&module.Module.properties,
&module.Module.deviceProperties,
@@ -198,3 +198,43 @@
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
return module
}
+
+type appTestProperties struct {
+ Instrumentation_for *string
+}
+
+type AndroidTest struct {
+ AndroidApp
+
+ appTestProperties appTestProperties
+
+ testProperties testProperties
+}
+
+func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if String(a.appTestProperties.Instrumentation_for) != "" {
+ a.AndroidApp.extraLinkFlags = append(a.AndroidApp.extraLinkFlags,
+ "--rename-instrumentation-target-package",
+ String(a.appTestProperties.Instrumentation_for))
+ }
+
+ a.generateAndroidBuildActions(ctx)
+}
+
+func AndroidTestFactory() android.Module {
+ module := &AndroidTest{}
+
+ module.Module.deviceProperties.Optimize.Enabled = proptools.BoolPtr(true)
+
+ module.AddProperties(
+ &module.Module.properties,
+ &module.Module.deviceProperties,
+ &module.Module.protoProperties,
+ &module.aaptProperties,
+ &module.appProperties,
+ &module.appTestProperties)
+
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+ return module
+}
diff --git a/java/java.go b/java/java.go
index 8c23124..02fdc00 100644
--- a/java/java.go
+++ b/java/java.go
@@ -171,6 +171,9 @@
// if not blank, set to the version of the sdk to compile against
Sdk_version *string
+ // if true, compile against the platform APIs instead of an SDK.
+ Platform_apis *bool
+
Aidl struct {
// Top level directories to pass to aidl tool
Include_dirs []string
@@ -209,8 +212,8 @@
}
Optimize struct {
- // If false, disable all optimization. Defaults to true for apps, false for
- // libraries and tests.
+ // If false, disable all optimization. Defaults to true for android_app and android_test
+ // modules, false for java_library and java_test modules.
Enabled *bool
// If true, optimize for size by removing unused code. Defaults to true for apps,
diff --git a/python/androidmk.go b/python/androidmk.go
index 5fa01ab..365b422 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -77,6 +77,7 @@
ret.OutputFile = android.OptionalPathForPath(installer.path)
}
+ ret.Required = append(ret.Required, "libc++")
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
path := installer.path.RelPathString()
dir, file := filepath.Split(path)
diff --git a/python/proto.go b/python/proto.go
index 42987fa..2370cd2 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -30,14 +30,14 @@
blueprint.RuleParams{
Command: `rm -rf $out.tmp && mkdir -p $out.tmp && ` +
`$protocCmd --python_out=$out.tmp --dependency_out=$out.d -I $protoBase $protoFlags $in && ` +
- `$parCmd -o $out -P $pkgPath -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
+ `$parCmd -o $out $pkgPathArgs -C $out.tmp -D $out.tmp && rm -rf $out.tmp`,
CommandDeps: []string{
"$protocCmd",
"$parCmd",
},
Depfile: "${out}.d",
Deps: blueprint.DepsGCC,
- }, "protoBase", "protoFlags", "pkgPath")
+ }, "protoBase", "protoFlags", "pkgPathArgs")
)
func genProto(ctx android.ModuleContext, p *android.ProtoProperties,
@@ -53,15 +53,19 @@
protoBase = strings.TrimSuffix(protoFile.String(), protoFile.Rel())
}
+ var pkgPathArgs string
+ if pkgPath != "" {
+ pkgPathArgs = "-P " + pkgPath
+ }
ctx.Build(pctx, android.BuildParams{
Rule: proto,
Description: "protoc " + protoFile.Rel(),
Output: srcJarFile,
Input: protoFile,
Args: map[string]string{
- "protoBase": protoBase,
- "protoFlags": strings.Join(protoFlags, " "),
- "pkgPath": pkgPath,
+ "protoBase": protoBase,
+ "protoFlags": strings.Join(protoFlags, " "),
+ "pkgPathArgs": pkgPathArgs,
},
})
diff --git a/python/python.go b/python/python.go
index a277988..6d6ac27 100644
--- a/python/python.go
+++ b/python/python.go
@@ -206,7 +206,7 @@
var (
pythonLibTag = dependencyTag{name: "pythonLib"}
launcherTag = dependencyTag{name: "launcher"}
- pyIdentifierRegexp = regexp.MustCompile(`^([a-z]|[A-Z]|_)([a-z]|[A-Z]|[0-9]|_)*$`)
+ pyIdentifierRegexp = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
pyExt = ".py"
protoExt = ".proto"
pyVersion2 = "PY2"
@@ -420,16 +420,20 @@
// pkg_path starts from "internal/" implicitly.
pkgPath = filepath.Join(internal, pkgPath)
} else {
- // pkg_path starts from "runfiles/" implicitly.
- pkgPath = filepath.Join(runFiles, pkgPath)
+ if !p.isEmbeddedLauncherEnabled(p.properties.Actual_version) {
+ // pkg_path starts from "runfiles/" implicitly.
+ pkgPath = filepath.Join(runFiles, pkgPath)
+ }
}
} else {
if p.properties.Is_internal != nil && *p.properties.Is_internal {
// pkg_path starts from "runfiles/" implicitly.
pkgPath = internal
} else {
- // pkg_path starts from "runfiles/" implicitly.
- pkgPath = runFiles
+ if !p.isEmbeddedLauncherEnabled(p.properties.Actual_version) {
+ // pkg_path starts from "runfiles/" implicitly.
+ pkgPath = runFiles
+ }
}
}
@@ -520,7 +524,9 @@
sort.Strings(keys)
parArgs := []string{}
- parArgs = append(parArgs, `-P `+pkgPath)
+ if pkgPath != "" {
+ parArgs = append(parArgs, `-P `+pkgPath)
+ }
implicits := android.Paths{}
for _, k := range keys {
parArgs = append(parArgs, `-C `+k)
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index b90a28b..386298e 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -18,8 +18,6 @@
for directory in search_path:
if directory == '': continue
path = os.path.join(directory, name)
- if os.path.islink(path):
- path = os.path.realpath(path)
# Check if path is actual executable file.
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index 5809894..1fe5b6f 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -13,9 +13,22 @@
// limitations under the License.
bootstrap_go_package {
+ name: "soong-ui-build-paths",
+ pkgPath: "android/soong/ui/build/paths",
+ srcs: [
+ "paths/config.go",
+ "paths/logs.go",
+ ],
+ testSrcs: [
+ "paths/logs_test.go",
+ ],
+}
+
+bootstrap_go_package {
name: "soong-ui-build",
pkgPath: "android/soong/ui/build",
deps: [
+ "soong-ui-build-paths",
"soong-ui-logger",
"soong-ui-tracer",
"soong-shared",
@@ -33,6 +46,7 @@
"finder.go",
"kati.go",
"ninja.go",
+ "path.go",
"proc_sync.go",
"signal.go",
"soong.go",
diff --git a/ui/build/build.go b/ui/build/build.go
index 78eb6a3..66dbf03 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -140,6 +140,8 @@
ensureEmptyDirectoriesExist(ctx, config.TempDir())
+ SetupPath(ctx, config)
+
if what&BuildProductConfig != 0 {
// Run make for product config
runMakeProductConfig(ctx, config)
diff --git a/ui/build/config.go b/ui/build/config.go
index 5622dff..6f2d24a 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -51,6 +51,8 @@
targetDeviceDir string
brokenDupRules bool
+
+ pathReplaced bool
}
const srcDirFileCheck = "build/soong/root.bp"
diff --git a/ui/build/path.go b/ui/build/path.go
new file mode 100644
index 0000000..52658ef
--- /dev/null
+++ b/ui/build/path.go
@@ -0,0 +1,149 @@
+// Copyright 2018 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 (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint/microfactory"
+
+ "android/soong/ui/build/paths"
+)
+
+func parsePathDir(dir string) []string {
+ f, err := os.Open(dir)
+ if err != nil {
+ return nil
+ }
+ defer f.Close()
+
+ if s, err := f.Stat(); err != nil || !s.IsDir() {
+ return nil
+ }
+
+ infos, err := f.Readdir(-1)
+ if err != nil {
+ return nil
+ }
+
+ ret := make([]string, 0, len(infos))
+ for _, info := range infos {
+ if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
+ ret = append(ret, info.Name())
+ }
+ }
+ return ret
+}
+
+func SetupPath(ctx Context, config Config) {
+ if config.pathReplaced {
+ return
+ }
+
+ ctx.BeginTrace("path")
+ defer ctx.EndTrace()
+
+ origPath, _ := config.Environment().Get("PATH")
+ myPath := filepath.Join(config.OutDir(), ".path")
+ interposer := myPath + "_interposer"
+
+ var cfg microfactory.Config
+ cfg.Map("android/soong", "build/soong")
+ cfg.TrimPath, _ = filepath.Abs(".")
+ if _, err := microfactory.Build(&cfg, interposer, "android/soong/cmd/path_interposer"); err != nil {
+ ctx.Fatalln("Failed to build path interposer:", err)
+ }
+
+ if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
+ ctx.Fatalln("Failed to write original path:", err)
+ }
+
+ entries, err := paths.LogListener(ctx.Context, interposer+"_log")
+ if err != nil {
+ ctx.Fatalln("Failed to listen for path logs:", err)
+ }
+
+ go func() {
+ for log := range entries {
+ curPid := os.Getpid()
+ for i, proc := range log.Parents {
+ if proc.Pid == curPid {
+ log.Parents = log.Parents[i:]
+ break
+ }
+ }
+ procPrints := []string{
+ "See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
+ }
+ if len(log.Parents) > 0 {
+ procPrints = append(procPrints, "Process tree:")
+ for i, proc := range log.Parents {
+ procPrints = append(procPrints, fmt.Sprintf("%s→ %s", strings.Repeat(" ", i), proc.Command))
+ }
+ }
+
+ config := paths.GetConfig(log.Basename)
+ if config.Error {
+ ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
+ for _, line := range procPrints {
+ ctx.Println(line)
+ }
+ } else {
+ ctx.Verbosef("Unknown PATH tool %q used: %#v", log.Basename, log.Args)
+ for _, line := range procPrints {
+ ctx.Verboseln(line)
+ }
+ }
+ }
+ }()
+
+ ensureEmptyDirectoriesExist(ctx, myPath)
+
+ var execs []string
+ for _, pathEntry := range filepath.SplitList(origPath) {
+ if pathEntry == "" {
+ // Ignore the current directory
+ continue
+ }
+ // TODO(dwillemsen): remove path entries under TOP? or anything
+ // that looks like an android source dir? They won't exist on
+ // the build servers, since they're added by envsetup.sh.
+ // (Except for the JDK, which is configured in ui/build/config.go)
+
+ execs = append(execs, parsePathDir(pathEntry)...)
+ }
+
+ allowAllSymlinks := config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS")
+ for _, name := range execs {
+ if !paths.GetConfig(name).Symlink && !allowAllSymlinks {
+ continue
+ }
+
+ err := os.Symlink("../.path_interposer", filepath.Join(myPath, name))
+ // Intentionally ignore existing files -- that means that we
+ // just created it, and the first one should win.
+ if err != nil && !os.IsExist(err) {
+ ctx.Fatalln("Failed to create symlink:", err)
+ }
+ }
+
+ myPath, _ = filepath.Abs(myPath)
+ config.Environment().Set("PATH", myPath)
+ config.pathReplaced = true
+}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
new file mode 100644
index 0000000..548b038
--- /dev/null
+++ b/ui/build/paths/config.go
@@ -0,0 +1,160 @@
+// Copyright 2018 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 paths
+
+import "runtime"
+
+type PathConfig struct {
+ // Whether to create the symlink in the new PATH for this tool.
+ Symlink bool
+
+ // Whether to log about usages of this tool to the soong.log
+ Log bool
+
+ // Whether to exit with an error instead of invoking the underlying tool.
+ Error bool
+}
+
+var Allowed = PathConfig{
+ Symlink: true,
+ Log: false,
+ Error: false,
+}
+
+var Forbidden = PathConfig{
+ Symlink: false,
+ Log: true,
+ Error: true,
+}
+
+// The configuration used if the tool is not listed in the config below.
+// Currently this will create the symlink, but log a warning. In the future,
+// I expect this to move closer to Forbidden.
+var Missing = PathConfig{
+ Symlink: true,
+ Log: true,
+ Error: false,
+}
+
+func GetConfig(name string) PathConfig {
+ if config, ok := Configuration[name]; ok {
+ return config
+ }
+ return Missing
+}
+
+var Configuration = map[string]PathConfig{
+ "awk": Allowed,
+ "basename": Allowed,
+ "bash": Allowed,
+ "bzip2": Allowed,
+ "cat": Allowed,
+ "chmod": Allowed,
+ "cmp": Allowed,
+ "comm": Allowed,
+ "cp": Allowed,
+ "cut": Allowed,
+ "date": Allowed,
+ "dd": Allowed,
+ "diff": Allowed,
+ "dirname": Allowed,
+ "echo": Allowed,
+ "egrep": Allowed,
+ "env": Allowed,
+ "expr": Allowed,
+ "find": Allowed,
+ "getconf": Allowed,
+ "getopt": Allowed,
+ "git": Allowed,
+ "grep": Allowed,
+ "gzip": Allowed,
+ "head": Allowed,
+ "hexdump": Allowed,
+ "hostname": Allowed,
+ "jar": Allowed,
+ "java": Allowed,
+ "javap": Allowed,
+ "ln": Allowed,
+ "ls": Allowed,
+ "m4": Allowed,
+ "make": Allowed,
+ "md5sum": Allowed,
+ "mkdir": Allowed,
+ "mktemp": Allowed,
+ "mv": Allowed,
+ "openssl": Allowed,
+ "patch": Allowed,
+ "perl": Allowed,
+ "pstree": Allowed,
+ "python": Allowed,
+ "python2.7": Allowed,
+ "python3": Allowed,
+ "readlink": Allowed,
+ "realpath": Allowed,
+ "rm": Allowed,
+ "rsync": Allowed,
+ "runalarm": Allowed,
+ "sed": Allowed,
+ "setsid": Allowed,
+ "sh": Allowed,
+ "sha256sum": Allowed,
+ "sha512sum": Allowed,
+ "sort": Allowed,
+ "stat": Allowed,
+ "sum": Allowed,
+ "tar": Allowed,
+ "tail": Allowed,
+ "touch": Allowed,
+ "tr": Allowed,
+ "true": Allowed,
+ "uname": Allowed,
+ "uniq": Allowed,
+ "unzip": Allowed,
+ "wc": Allowed,
+ "which": Allowed,
+ "whoami": Allowed,
+ "xargs": Allowed,
+ "xmllint": Allowed,
+ "xz": Allowed,
+ "zip": Allowed,
+ "zipinfo": Allowed,
+
+ // Host toolchain is removed. In-tree toolchain should be used instead.
+ // GCC also can't find cc1 with this implementation.
+ "ar": Forbidden,
+ "as": Forbidden,
+ "cc": Forbidden,
+ "clang": Forbidden,
+ "clang++": Forbidden,
+ "gcc": Forbidden,
+ "g++": Forbidden,
+ "ld": Forbidden,
+ "ld.bfd": Forbidden,
+ "ld.gold": Forbidden,
+ "pkg-config": Forbidden,
+
+ // We've got prebuilts of these
+ //"dtc": Forbidden,
+ //"lz4": Forbidden,
+ //"lz4c": Forbidden,
+}
+
+func init() {
+ if runtime.GOOS == "darwin" {
+ Configuration["md5"] = Allowed
+ Configuration["sw_vers"] = Allowed
+ Configuration["xcrun"] = Allowed
+ }
+}
diff --git a/ui/build/paths/logs.go b/ui/build/paths/logs.go
new file mode 100644
index 0000000..6b520aa
--- /dev/null
+++ b/ui/build/paths/logs.go
@@ -0,0 +1,202 @@
+// Copyright 2018 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 paths
+
+import (
+ "context"
+ "encoding/gob"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+ "runtime"
+ "syscall"
+ "time"
+)
+
+type LogProcess struct {
+ Pid int
+ Command string
+}
+
+type LogEntry struct {
+ Basename string
+ Args []string
+ Parents []LogProcess
+}
+
+const timeoutDuration = time.Duration(100) * time.Millisecond
+
+type socketAddrFunc func(string) (string, func(), error)
+
+func procFallback(name string) (string, func(), error) {
+ d, err := os.Open(filepath.Dir(name))
+ if err != nil {
+ return "", nil, err
+ }
+
+ return fmt.Sprintf("/proc/self/fd/%d/%s", d.Fd(), filepath.Base(name)), func() {
+ d.Close()
+ }, nil
+}
+
+func tmpFallback(name string) (addr string, cleanup func(), err error) {
+ d, err := ioutil.TempDir("/tmp", "log_sock")
+ if err != nil {
+ cleanup = func() {}
+ return
+ }
+ cleanup = func() {
+ os.RemoveAll(d)
+ }
+
+ dir := filepath.Dir(name)
+
+ absDir, err := filepath.Abs(dir)
+ if err != nil {
+ return
+ }
+
+ err = os.Symlink(absDir, filepath.Join(d, "d"))
+ if err != nil {
+ return
+ }
+
+ addr = filepath.Join(d, "d", filepath.Base(name))
+
+ return
+}
+
+func getSocketAddr(name string) (string, func(), error) {
+ maxNameLen := len(syscall.RawSockaddrUnix{}.Path)
+
+ if len(name) < maxNameLen {
+ return name, func() {}, nil
+ }
+
+ if runtime.GOOS == "linux" {
+ addr, cleanup, err := procFallback(name)
+ if err == nil {
+ if len(addr) < maxNameLen {
+ return addr, cleanup, nil
+ }
+ }
+ cleanup()
+ }
+
+ addr, cleanup, err := tmpFallback(name)
+ if err == nil {
+ if len(addr) < maxNameLen {
+ return addr, cleanup, nil
+ }
+ }
+ cleanup()
+
+ return name, func() {}, fmt.Errorf("Path to socket is still over size limit, fallbacks failed.")
+}
+
+func dial(name string, lookup socketAddrFunc) (net.Conn, error) {
+ socket, cleanup, err := lookup(name)
+ defer cleanup()
+ if err != nil {
+ return nil, err
+ }
+
+ dialer := &net.Dialer{
+ Timeout: timeoutDuration,
+ }
+ return dialer.Dial("unix", socket)
+}
+
+func listen(name string, lookup socketAddrFunc) (net.Listener, error) {
+ socket, cleanup, err := lookup(name)
+ defer cleanup()
+ if err != nil {
+ return nil, err
+ }
+
+ return net.Listen("unix", socket)
+}
+
+func SendLog(logSocket string, entry *LogEntry, done chan interface{}) {
+ sendLog(logSocket, getSocketAddr, entry, done)
+}
+
+func sendLog(logSocket string, lookup socketAddrFunc, entry *LogEntry, done chan interface{}) {
+ defer close(done)
+
+ conn, err := dial(logSocket, lookup)
+ if err != nil {
+ return
+ }
+ defer conn.Close()
+
+ conn.SetDeadline(time.Now().Add(timeoutDuration))
+
+ enc := gob.NewEncoder(conn)
+ enc.Encode(entry)
+}
+
+func LogListener(ctx context.Context, logSocket string) (chan *LogEntry, error) {
+ return logListener(ctx, logSocket, getSocketAddr)
+}
+
+func logListener(ctx context.Context, logSocket string, lookup socketAddrFunc) (chan *LogEntry, error) {
+ ret := make(chan *LogEntry, 5)
+
+ if err := os.Remove(logSocket); err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+
+ ln, err := listen(logSocket, lookup)
+ if err != nil {
+ return nil, err
+ }
+
+ go func() {
+ for {
+ select {
+ case <-ctx.Done():
+ ln.Close()
+ }
+ }
+ }()
+
+ go func() {
+ defer close(ret)
+
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ ln.Close()
+ break
+ }
+ conn.SetDeadline(time.Now().Add(timeoutDuration))
+
+ go func() {
+ defer conn.Close()
+
+ dec := gob.NewDecoder(conn)
+ entry := &LogEntry{}
+ if err := dec.Decode(entry); err != nil {
+ return
+ }
+ ret <- entry
+ }()
+ }
+ }()
+ return ret, nil
+}
diff --git a/ui/build/paths/logs_test.go b/ui/build/paths/logs_test.go
new file mode 100644
index 0000000..792b3b3
--- /dev/null
+++ b/ui/build/paths/logs_test.go
@@ -0,0 +1,167 @@
+// Copyright 2018 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 paths
+
+import (
+ "context"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestSendLog(t *testing.T) {
+ t.Run("Short name", func(t *testing.T) {
+ d, err := ioutil.TempDir("", "s")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(d)
+ f := filepath.Join(d, "s")
+
+ testSendLog(t, f, getSocketAddr)
+ })
+
+ testLongName := func(t *testing.T, lookup socketAddrFunc) {
+ d, err := ioutil.TempDir("", strings.Repeat("s", 150))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(d)
+ f := filepath.Join(d, strings.Repeat("s", 10))
+
+ testSendLog(t, f, lookup)
+ }
+
+ // Using a name longer than the ~100 limit of the underlying calls to bind, etc
+ t.Run("Long name", func(t *testing.T) {
+ testLongName(t, getSocketAddr)
+ })
+
+ if runtime.GOOS == "linux" {
+ t.Run("Long name proc fallback", func(t *testing.T) {
+ testLongName(t, procFallback)
+ })
+ }
+
+ t.Run("Long name tmp fallback", func(t *testing.T) {
+ testLongName(t, tmpFallback)
+ })
+}
+
+func testSendLog(t *testing.T, socket string, lookup socketAddrFunc) {
+ ctx, _ := context.WithTimeout(context.Background(), 2*timeoutDuration)
+
+ recv, err := logListener(ctx, socket, lookup)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ go func() {
+ for i := 0; i < 10; i++ {
+ sendLog(socket, lookup, &LogEntry{
+ Basename: "test",
+ Args: []string{"foo", "bar"},
+ }, make(chan interface{}))
+ }
+ }()
+
+ count := 0
+ for {
+ select {
+ case entry := <-recv:
+ ref := LogEntry{
+ Basename: "test",
+ Args: []string{"foo", "bar"},
+ }
+ if !reflect.DeepEqual(ref, *entry) {
+ t.Fatalf("Bad log entry: %v", entry)
+ }
+ count++
+
+ if count == 10 {
+ return
+ }
+ case <-ctx.Done():
+ t.Error("Hit timeout before receiving all logs")
+ }
+ }
+}
+
+func TestSendLogError(t *testing.T) {
+ d, err := ioutil.TempDir("", "log_socket")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(d)
+
+ t.Run("Missing file", func(t *testing.T) {
+ start := time.Now()
+ SendLog(filepath.Join(d, "missing"), &LogEntry{}, make(chan interface{}))
+ elapsed := time.Since(start)
+ if elapsed > timeoutDuration {
+ t.Errorf("Should have been << timeout (%s), but was %s", timeoutDuration, elapsed)
+ }
+ })
+
+ t.Run("Regular file", func(t *testing.T) {
+ f := filepath.Join(d, "file")
+ if fp, err := os.Create(f); err == nil {
+ fp.Close()
+ } else {
+ t.Fatal(err)
+ }
+
+ start := time.Now()
+ SendLog(f, &LogEntry{}, make(chan interface{}))
+ elapsed := time.Since(start)
+ if elapsed > timeoutDuration {
+ t.Errorf("Should have been << timeout (%s), but was %s", timeoutDuration, elapsed)
+ }
+ })
+
+ t.Run("Reader not reading", func(t *testing.T) {
+ f := filepath.Join(d, "sock1")
+
+ ln, err := net.Listen("unix", f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ done := make(chan bool, 1)
+ go func() {
+ for i := 0; i < 10; i++ {
+ SendLog(f, &LogEntry{
+ // Ensure a relatively large payload
+ Basename: strings.Repeat(" ", 100000),
+ }, make(chan interface{}))
+ }
+ done <- true
+ }()
+
+ select {
+ case <-done:
+ break
+ case <-time.After(12 * timeoutDuration):
+ t.Error("Should have finished")
+ }
+ })
+}
diff --git a/ui/build/sandbox/darwin/global.sb b/ui/build/sandbox/darwin/global.sb
index 47d0c43..e32b64b 100644
--- a/ui/build/sandbox/darwin/global.sb
+++ b/ui/build/sandbox/darwin/global.sb
@@ -35,6 +35,12 @@
(global-name-regex #"^com\.apple\.distributed_notifications") ; xcodebuild in Soong
)
+; Allow suid /bin/ps to function
+(allow process-exec (literal "/bin/ps") (with no-sandbox))
+
+; Allow path_interposer unix domain socket without logging
+(allow network-outbound (literal (string-append (param "OUT_DIR") "/.path_interposer_log")))
+
; Allow executing any file
(allow process-exec*)
(allow process-fork)