From 62abd12ba573d123cda34515a75f6e228f6cadc4 Mon Sep 17 00:00:00 2001 From: Kiyoung Kim Date: Tue, 6 Oct 2020 17:16:44 +0900 Subject: [PATCH] Handle new filetype 'linker_config' Handle new filetype 'linker_config' which is configuration for linkerconfig in json type and convert into protobuf in build time. Bug: 169634881 Test: Build succeeded and cuttlefish boot succeeded Change-Id: I56555fc738e6d6600d15a191a24f79a2ee747f52 --- linkerconfig/Android.bp | 17 ++++ linkerconfig/linkerconfig.go | 108 +++++++++++++++++++++ linkerconfig/linkerconfig_test.go | 127 +++++++++++++++++++++++++ linkerconfig/proto/Android.bp | 28 ++++++ linkerconfig/proto/OWNERS | 3 + linkerconfig/proto/linker_config.proto | 31 ++++++ scripts/Android.bp | 19 ++++ scripts/OWNERS | 1 + scripts/conv_linker_config.py | 81 ++++++++++++++++ 9 files changed, 415 insertions(+) create mode 100644 linkerconfig/Android.bp create mode 100644 linkerconfig/linkerconfig.go create mode 100644 linkerconfig/linkerconfig_test.go create mode 100644 linkerconfig/proto/Android.bp create mode 100644 linkerconfig/proto/OWNERS create mode 100644 linkerconfig/proto/linker_config.proto create mode 100644 scripts/conv_linker_config.py diff --git a/linkerconfig/Android.bp b/linkerconfig/Android.bp new file mode 100644 index 000000000..8807a2e68 --- /dev/null +++ b/linkerconfig/Android.bp @@ -0,0 +1,17 @@ +bootstrap_go_package { + name: "soong-linkerconfig", + pkgPath: "android/soong/linkerconfig", + deps: [ + "blueprint", + "soong", + "soong-android", + "soong-etc", + ], + srcs: [ + "linkerconfig.go", + ], + testSrcs: [ + "linkerconfig_test.go", + ], + pluginFor: ["soong_build"], +} diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go new file mode 100644 index 000000000..1c44c746f --- /dev/null +++ b/linkerconfig/linkerconfig.go @@ -0,0 +1,108 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// 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 linkerconfig + +import ( + "android/soong/android" + "android/soong/etc" + + "github.com/google/blueprint/proptools" +) + +var ( + pctx = android.NewPackageContext("android/soong/linkerconfig") +) + +func init() { + pctx.HostBinToolVariable("conv_linker_config", "conv_linker_config") + android.RegisterModuleType("linker_config", linkerConfigFactory) +} + +type linkerConfigProperties struct { + // source linker configuration property file + Src *string `android:"path"` + + // If set to true, allow module to be installed to one of the partitions. + // Default value is true. + // Installable should be marked as false for APEX configuration to avoid + // conflicts of configuration on /system/etc directory. + Installable *bool +} + +type linkerConfig struct { + android.ModuleBase + properties linkerConfigProperties + + outputFilePath android.OutputPath + installDirPath android.InstallPath +} + +// Implement PrebuiltEtcModule interface to fit in APEX prebuilt list. +var _ etc.PrebuiltEtcModule = (*linkerConfig)(nil) + +func (l *linkerConfig) BaseDir() string { + return "etc" +} + +func (l *linkerConfig) SubDir() string { + return "" +} + +func (l *linkerConfig) OutputFile() android.OutputPath { + return l.outputFilePath +} + +func (l *linkerConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) { + inputFile := android.PathForModuleSrc(ctx, android.String(l.properties.Src)) + l.outputFilePath = android.PathForModuleOut(ctx, "linker.config.pb").OutputPath + l.installDirPath = android.PathForModuleInstall(ctx, "etc") + linkerConfigRule := android.NewRuleBuilder() + linkerConfigRule.Command(). + BuiltTool(ctx, "conv_linker_config"). + Flag("proto"). + FlagWithInput("-s ", inputFile). + FlagWithOutput("-o ", l.outputFilePath) + linkerConfigRule.Build(pctx, ctx, "conv_linker_config", + "Generate linker config protobuf "+l.outputFilePath.String()) + + if proptools.BoolDefault(l.properties.Installable, true) { + ctx.InstallFile(l.installDirPath, l.outputFilePath.Base(), l.outputFilePath) + } +} + +// linker_config generates protobuf file from json file. This protobuf file will be used from +// linkerconfig while generating ld.config.txt. Format of this file can be found from +// https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md +func linkerConfigFactory() android.Module { + m := &linkerConfig{} + m.AddProperties(&m.properties) + android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibFirst) + return m +} + +func (l *linkerConfig) AndroidMkEntries() []android.AndroidMkEntries { + installable := proptools.BoolDefault(l.properties.Installable, true) + return []android.AndroidMkEntries{android.AndroidMkEntries{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(l.outputFilePath), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base()) + entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable) + }, + }, + }} +} diff --git a/linkerconfig/linkerconfig_test.go b/linkerconfig/linkerconfig_test.go new file mode 100644 index 000000000..13c276abf --- /dev/null +++ b/linkerconfig/linkerconfig_test.go @@ -0,0 +1,127 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// 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 linkerconfig + +import ( + "android/soong/android" + "io/ioutil" + "os" + "reflect" + "testing" +) + +var buildDir string + +func setUp() { + var err error + buildDir, err = ioutil.TempDir("", "soong_etc_test") + if err != nil { + panic(err) + } +} + +func tearDown() { + os.RemoveAll(buildDir) +} + +func TestMain(m *testing.M) { + run := func() int { + setUp() + defer tearDown() + + return m.Run() + } + + os.Exit(run()) +} + +func testContext(t *testing.T, bp string) (*android.TestContext, android.Config) { + t.Helper() + + fs := map[string][]byte{ + "linker.config.json": nil, + } + + config := android.TestArchConfig(buildDir, nil, bp, fs) + + ctx := android.NewTestArchContext() + ctx.RegisterModuleType("linker_config", linkerConfigFactory) + ctx.Register(config) + + _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) + android.FailIfErrored(t, errs) + _, errs = ctx.PrepareBuildActions(config) + android.FailIfErrored(t, errs) + + return ctx, config +} + +func TestBaseLinkerConfig(t *testing.T) { + ctx, config := testContext(t, ` + linker_config { + name: "linker-config-base", + src: "linker.config.json", + } + `) + + expected := map[string][]string{ + "LOCAL_MODULE": {"linker-config-base"}, + "LOCAL_MODULE_CLASS": {"ETC"}, + "LOCAL_INSTALLED_MODULE_STEM": {"linker.config.pb"}, + } + + p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig) + + if p.outputFilePath.Base() != "linker.config.pb" { + t.Errorf("expected linker.config.pb, got %q", p.outputFilePath.Base()) + } + + entries := android.AndroidMkEntriesForTest(t, config, "", p)[0] + for k, expectedValue := range expected { + if value, ok := entries.EntryMap[k]; ok { + if !reflect.DeepEqual(value, expectedValue) { + t.Errorf("Value of %s is '%s', but expected as '%s'", k, value, expectedValue) + } + } else { + t.Errorf("%s is not defined", k) + } + } + + if value, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok { + t.Errorf("Value of LOCAL_UNINSTALLABLE_MODULE is %s, but expected as empty", value) + } +} + +func TestUninstallableLinkerConfig(t *testing.T) { + ctx, config := testContext(t, ` + linker_config { + name: "linker-config-base", + src: "linker.config.json", + installable: false, + } + `) + + expected := []string{"true"} + + p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig) + entries := android.AndroidMkEntriesForTest(t, config, "", p)[0] + if value, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok { + if !reflect.DeepEqual(value, expected) { + t.Errorf("LOCAL_UNINSTALLABLE_MODULE is expected to be true but %s", value) + } + } else { + t.Errorf("LOCAL_UNINSTALLABLE_MODULE is not defined") + } +} diff --git a/linkerconfig/proto/Android.bp b/linkerconfig/proto/Android.bp new file mode 100644 index 000000000..c04887e9b --- /dev/null +++ b/linkerconfig/proto/Android.bp @@ -0,0 +1,28 @@ +cc_library_static { + name: "lib_linker_config_proto_lite", + host_supported: true, + recovery_available: true, + proto: { + export_proto_headers: true, + type: "lite", + }, + srcs: ["linker_config.proto"], +} + +python_library_host { + name: "linker_config_proto", + version: { + py2: { + enabled: false, + }, + py3: { + enabled: true, + }, + }, + srcs: [ + "linker_config.proto", + ], + proto: { + canonical_path_from_root: false, + }, +} diff --git a/linkerconfig/proto/OWNERS b/linkerconfig/proto/OWNERS new file mode 100644 index 000000000..31f0460a1 --- /dev/null +++ b/linkerconfig/proto/OWNERS @@ -0,0 +1,3 @@ +kiyoungkim@google.com +jiyong@google.com +jooyung@google.com diff --git a/linkerconfig/proto/linker_config.proto b/linkerconfig/proto/linker_config.proto new file mode 100644 index 000000000..91a596824 --- /dev/null +++ b/linkerconfig/proto/linker_config.proto @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * 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. + */ + +// This format file defines configuration file for linkerconfig. Details on this +// format can be found from +// https://android.googlesource.com/platform/system/linkerconfig/+/master/README.md + +syntax = "proto3"; + +package android.linkerconfig.proto; + +message LinkerConfig { + // Extra permitted paths + repeated string permittedPaths = 1; + + // Force APEX namespace visible + bool visible = 2; +} diff --git a/scripts/Android.bp b/scripts/Android.bp index 7782c6862..92f5c5335 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -206,3 +206,22 @@ python_binary_host { "ninja_rsp.py", ], } + +python_binary_host { + name: "conv_linker_config", + srcs: [ + "conv_linker_config.py", + ], + version: { + py2: { + enabled: false, + }, + py3: { + enabled: true, + embedded_launcher: true, + }, + }, + libs: [ + "linker_config_proto", + ], +} diff --git a/scripts/OWNERS b/scripts/OWNERS index 8c644247e..819808362 100644 --- a/scripts/OWNERS +++ b/scripts/OWNERS @@ -2,3 +2,4 @@ per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@go per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com +per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py new file mode 100644 index 000000000..86f788d81 --- /dev/null +++ b/scripts/conv_linker_config.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# +# Copyright (C) 2020 The Android Open Source Project +# +# 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. +""" A tool to convert json file into pb with linker config format.""" + +import argparse +import collections +import json + +import linker_config_pb2 +from google.protobuf.json_format import ParseDict +from google.protobuf.text_format import MessageToString + + +def Proto(args): + with open(args.source) as f: + obj = json.load(f, object_pairs_hook=collections.OrderedDict) + pb = ParseDict(obj, linker_config_pb2.LinkerConfig()) + with open(args.output, 'wb') as f: + f.write(pb.SerializeToString()) + + +def Print(args): + with open(args.source, 'rb') as f: + pb = linker_config_pb2.LinkerConfig() + pb.ParseFromString(f.read()) + print(MessageToString(pb)) + + +def GetArgParser(): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers() + + parser_proto = subparsers.add_parser( + 'proto', help='Convert the input JSON configuration file into protobuf.') + parser_proto.add_argument( + '-s', + '--source', + required=True, + type=str, + help='Source linker configuration file in JSON.') + parser_proto.add_argument( + '-o', + '--output', + required=True, + type=str, + help='Target path to create protobuf file.') + parser_proto.set_defaults(func=Proto) + + print_proto = subparsers.add_parser( + 'print', help='Print configuration in human-readable text format.') + print_proto.add_argument( + '-s', + '--source', + required=True, + type=str, + help='Source linker configuration file in protobuf.') + print_proto.set_defaults(func=Print) + + return parser + + +def main(): + args = GetArgParser().parse_args() + args.func(args) + + +if __name__ == '__main__': + main()