diff options
-rw-r--r-- | Android.bp | 18 | ||||
-rw-r--r-- | android/prebuilt_etc.go | 62 | ||||
-rw-r--r-- | java/java_test.go | 2 | ||||
-rw-r--r-- | java/sdk_library.go | 4 | ||||
-rw-r--r-- | xml/xml.go | 136 | ||||
-rw-r--r-- | xml/xml_test.go | 86 |
6 files changed, 280 insertions, 28 deletions
@@ -291,6 +291,24 @@ bootstrap_go_package { ], } +bootstrap_go_package { + name: "soong-xml", + pkgPath: "android/soong/xml", + deps: [ + "blueprint", + "blueprint-pathtools", + "soong", + "soong-android", + ], + srcs: [ + "xml/xml.go", + ], + testSrcs: [ + "xml/xml_test.go", + ], + pluginFor: ["soong_build"], +} + // // Defaults to enable various configurations of host bionic // diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go index fee2c494..55a72a6a 100644 --- a/android/prebuilt_etc.go +++ b/android/prebuilt_etc.go @@ -28,45 +28,47 @@ func init() { type prebuiltEtcProperties struct { // Source file of this prebuilt. - Srcs []string `android:"arch_variant"` + Src *string `android:"arch_variant"` // optional subdirectory under which this file is installed into Sub_dir *string `android:"arch_variant"` } -type prebuiltEtc struct { +type PrebuiltEtc struct { ModuleBase - prebuilt Prebuilt properties prebuiltEtcProperties - sourceFilePath Path - installDirPath OutputPath + sourceFilePath Path + installDirPath OutputPath + additionalDependencies *Paths } -func (p *prebuiltEtc) Prebuilt() *Prebuilt { - return &p.prebuilt -} - -func (p *prebuiltEtc) DepsMutator(ctx BottomUpMutatorContext) { - if len(p.properties.Srcs) == 0 { - ctx.PropertyErrorf("srcs", "missing prebuilt source file") - } - - if len(p.properties.Srcs) > 1 { - ctx.PropertyErrorf("srcs", "multiple prebuilt source files") +func (p *PrebuiltEtc) DepsMutator(ctx BottomUpMutatorContext) { + if p.properties.Src == nil { + ctx.PropertyErrorf("src", "missing prebuilt source file") } // To support ":modulename" in src - ExtractSourceDeps(ctx, &(p.properties.Srcs)[0]) + ExtractSourceDeps(ctx, p.properties.Src) +} + +func (p *PrebuiltEtc) SourceFilePath(ctx ModuleContext) Path { + return ctx.ExpandSource(String(p.properties.Src), "src") } -func (p *prebuiltEtc) GenerateAndroidBuildActions(ctx ModuleContext) { - p.sourceFilePath = ctx.ExpandSource(p.properties.Srcs[0], "srcs") +// This allows other derivative modules (e.g. prebuilt_etc_xml) to perform +// additional steps (like validating the src) before the file is installed. +func (p *PrebuiltEtc) SetAdditionalDependencies(paths Paths) { + p.additionalDependencies = &paths +} + +func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx ModuleContext) { + p.sourceFilePath = ctx.ExpandSource(String(p.properties.Src), "src") p.installDirPath = PathForModuleInstall(ctx, "etc", String(p.properties.Sub_dir)) } -func (p *prebuiltEtc) AndroidMk() AndroidMkData { +func (p *PrebuiltEtc) AndroidMk() AndroidMkData { return AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) { fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)") @@ -76,16 +78,26 @@ func (p *prebuiltEtc) AndroidMk() AndroidMkData { fmt.Fprintln(w, "LOCAL_MODULE_TAGS := optional") fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", p.sourceFilePath.String()) fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", "$(OUT_DIR)/"+p.installDirPath.RelPathString()) + if p.additionalDependencies != nil { + fmt.Fprint(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=") + for _, path := range *p.additionalDependencies { + fmt.Fprint(w, " "+path.String()) + } + fmt.Fprintln(w, "") + } fmt.Fprintln(w, "include $(BUILD_PREBUILT)") }, } } -func PrebuiltEtcFactory() Module { - module := &prebuiltEtc{} - module.AddProperties(&module.properties) +func InitPrebuiltEtcModule(p *PrebuiltEtc) { + p.AddProperties(&p.properties) +} - InitPrebuiltModule(module, &(module.properties.Srcs)) - InitAndroidModule(module) +func PrebuiltEtcFactory() Module { + module := &PrebuiltEtc{} + InitPrebuiltEtcModule(module) + // This module is device-only + InitAndroidArchModule(module, DeviceSupported, MultilibCommon) return module } diff --git a/java/java_test.go b/java/java_test.go index de514e04..ea524962 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1040,7 +1040,7 @@ func TestJavaSdkLibrary(t *testing.T) { ctx.ModuleForTests("foo"+sdkDocsSuffix, "android_common") ctx.ModuleForTests("foo"+sdkDocsSuffix+sdkSystemApiSuffix, "android_common") ctx.ModuleForTests("foo"+sdkImplLibrarySuffix, "android_common") - ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "") + ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common") bazJavac := ctx.ModuleForTests("baz", "android_common").Rule("javac") // tests if baz is actually linked to the stubs lib diff --git a/java/sdk_library.go b/java/sdk_library.go index 13a9275c..2396467f 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -407,14 +407,14 @@ func (module *sdkLibrary) createXmlFile(mctx android.TopDownMutatorContext) { // <partition>/etc/permissions etcProps := struct { Name *string - Srcs []string + Src *string Sub_dir *string Soc_specific *bool Device_specific *bool Product_specific *bool }{} etcProps.Name = proptools.StringPtr(module.xmlFileName()) - etcProps.Srcs = []string{":" + module.xmlFileName() + "-gen"} + etcProps.Src = proptools.StringPtr(":" + module.xmlFileName() + "-gen") etcProps.Sub_dir = proptools.StringPtr("permissions") if module.SocSpecific() { etcProps.Soc_specific = proptools.BoolPtr(true) diff --git a/xml/xml.go b/xml/xml.go new file mode 100644 index 00000000..218d73ce --- /dev/null +++ b/xml/xml.go @@ -0,0 +1,136 @@ +// 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 xml + +import ( + "android/soong/android" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" +) + +// prebuilt_etc_xml installs an xml file under <partition>/etc/<subdir>. +// It also optionally validates the xml file against the schema. + +var ( + pctx = android.NewPackageContext("android/soong/xml") + + xmllintDtd = pctx.AndroidStaticRule("xmllint-dtd", + blueprint.RuleParams{ + Command: `$XmlLintCmd --dtdvalid $dtd $in > /dev/null && touch -a $out`, + CommandDeps: []string{"$XmlLintCmd"}, + Restat: true, + }, + "dtd") + + xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", + blueprint.RuleParams{ + Command: `$XmlLintCmd --schema $xsd $in > /dev/null && touch -a $out`, + CommandDeps: []string{"$XmlLintCmd"}, + Restat: true, + }, + "xsd") + + xmllintMinimal = pctx.AndroidStaticRule("xmllint-minimal", + blueprint.RuleParams{ + Command: `$XmlLintCmd $in > /dev/null && touch -a $out`, + CommandDeps: []string{"$XmlLintCmd"}, + Restat: true, + }) +) + +func init() { + android.RegisterModuleType("prebuilt_etc_xml", PrebuiltEtcXmlFactory) + pctx.HostBinToolVariable("XmlLintCmd", "xmllint") +} + +type prebuiltEtcXmlProperties struct { + // Optional DTD that will be used to validate the xml file. + Schema *string +} + +type prebuiltEtcXml struct { + android.PrebuiltEtc + + properties prebuiltEtcXmlProperties +} + +func (p *prebuiltEtcXml) timestampFilePath(ctx android.ModuleContext) android.WritablePath { + return android.PathForModuleOut(ctx, p.PrebuiltEtc.SourceFilePath(ctx).Base()+"-timestamp") +} + +func (p *prebuiltEtcXml) DepsMutator(ctx android.BottomUpMutatorContext) { + p.PrebuiltEtc.DepsMutator(ctx) + + // To support ":modulename" in schema + android.ExtractSourceDeps(ctx, p.properties.Schema) +} + +func (p *prebuiltEtcXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { + p.PrebuiltEtc.GenerateAndroidBuildActions(ctx) + + if p.properties.Schema != nil { + schema := ctx.ExpandSource(proptools.String(p.properties.Schema), "schema") + + switch schema.Ext() { + case ".dtd": + ctx.Build(pctx, android.BuildParams{ + Rule: xmllintDtd, + Description: "xmllint-dtd", + Input: p.PrebuiltEtc.SourceFilePath(ctx), + Output: p.timestampFilePath(ctx), + Implicit: schema, + Args: map[string]string{ + "dtd": schema.String(), + }, + }) + break + case ".xsd": + ctx.Build(pctx, android.BuildParams{ + Rule: xmllintXsd, + Description: "xmllint-xsd", + Input: p.PrebuiltEtc.SourceFilePath(ctx), + Output: p.timestampFilePath(ctx), + Implicit: schema, + Args: map[string]string{ + "xsd": schema.String(), + }, + }) + break + default: + ctx.PropertyErrorf("schema", "not supported extension: %q", schema.Ext()) + } + } else { + // when schema is not specified, just check if the xml is well-formed + ctx.Build(pctx, android.BuildParams{ + Rule: xmllintMinimal, + Description: "xmllint-minimal", + Input: p.PrebuiltEtc.SourceFilePath(ctx), + Output: p.timestampFilePath(ctx), + }) + } + + p.SetAdditionalDependencies([]android.Path{p.timestampFilePath(ctx)}) +} + +func PrebuiltEtcXmlFactory() android.Module { + module := &prebuiltEtcXml{} + module.AddProperties(&module.properties) + + android.InitPrebuiltEtcModule(&module.PrebuiltEtc) + // This module is device-only + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + return module +} diff --git a/xml/xml_test.go b/xml/xml_test.go new file mode 100644 index 00000000..e8fa49c2 --- /dev/null +++ b/xml/xml_test.go @@ -0,0 +1,86 @@ +// 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 xml + +import ( + "android/soong/android" + "io/ioutil" + "os" + "testing" +) + +func testXml(t *testing.T, bp string) *android.TestContext { + config, buildDir := setup(t) + defer teardown(buildDir) + ctx := android.NewTestArchContext() + ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory)) + ctx.RegisterModuleType("prebuilt_etc_xml", android.ModuleFactoryAdaptor(PrebuiltEtcXmlFactory)) + ctx.Register() + mockFiles := map[string][]byte{ + "Android.bp": []byte(bp), + "foo.xml": nil, + "foo.dtd": nil, + "bar.xml": nil, + "bar.xsd": nil, + } + ctx.MockFileSystem(mockFiles) + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + android.FailIfErrored(t, errs) + _, errs = ctx.PrepareBuildActions(config) + android.FailIfErrored(t, errs) + + return ctx +} + +func setup(t *testing.T) (config android.Config, buildDir string) { + buildDir, err := ioutil.TempDir("", "soong_xml_test") + if err != nil { + t.Fatal(err) + } + + config = android.TestArchConfig(buildDir, nil) + + return +} + +func teardown(buildDir string) { + os.RemoveAll(buildDir) +} + +// Minimal test +func TestPrebuiltEtcXml(t *testing.T) { + ctx := testXml(t, ` + prebuilt_etc_xml { + name: "foo.xml", + src: "foo.xml", + schema: "foo.dtd", + } + prebuilt_etc_xml { + name: "bar.xml", + src: "bar.xml", + schema: "bar.xsd", + } + `) + + xmllint := ctx.ModuleForTests("foo.xml", "android_common").Rule("xmllint") + input := xmllint.Input.String() + if input != "foo.xml" { + t.Errorf("input expected %q != got %q", "foo.xml", input) + } + schema := xmllint.Args["dtd"] + if schema != "foo.dtd" { + t.Errorf("dtd expected %q != got %q", "foo.dtdl", schema) + } +} |