Add HIDL Soong plugin.

Soong will now generate hidl-gen build rules instead of
having huge autogenerated build files.

Every hal can now be declared with 'hidl_interface' and
every package can be declared with 'hidl_package_root'.

Test: Modules build and work.
Bug: 35570956
Change-Id: I223c0799c5adb991f82a8970003e6fbb531f719f
diff --git a/build/Android.bp b/build/Android.bp
new file mode 100644
index 0000000..9b290a3
--- /dev/null
+++ b/build/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2017 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.
+
+bootstrap_go_package {
+    name: "hidl-soong-rules",
+    pkgPath: "android/soong/hidl",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-cc",
+        "soong-genrule",
+        "soong-java",
+    ],
+    srcs: [
+        "fqName.go",
+        "hidl_interface.go",
+        "hidl_package_root.go",
+        "properties.go",
+        "utils.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/build/fqName.go b/build/fqName.go
new file mode 100644
index 0000000..88f7dc0
--- /dev/null
+++ b/build/fqName.go
@@ -0,0 +1,126 @@
+// Copyright (C) 2017 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 hidl
+
+import (
+	"errors"
+	"path/filepath"
+	"regexp"
+	"strings"
+)
+
+type fqName struct {
+	packageComponents []string
+	minor             string
+	major             string
+}
+
+// See system/tools/hidl/FQName.h
+var component = "[a-zA-Z_][a-zA-Z_0-9]*"
+var pkg = "(" + component + "(?:[.]" + component + ")*)"
+var digits = "([0-9]+)"
+var re_package = regexp.MustCompile("^" + pkg + "@" + digits + "." + digits + "$")
+
+func parseFqName(f string) (*fqName, error) {
+	matches := re_package.FindAllStringSubmatch(f, 3)
+
+	if matches == nil {
+		return nil, errors.New("Poorly formed hal interface name: '" + f + "' must match '" + re_package.String() + "'")
+	}
+
+	ret := fqName{
+		packageComponents: strings.Split(matches[0][1], "."),
+		major:             matches[0][2],
+		minor:             matches[0][3],
+	}
+
+	return &ret, nil
+}
+
+func (f *fqName) inPackage(pkg string) bool {
+	components := strings.Split(pkg, ".")
+
+	if len(components) > len(f.packageComponents) {
+		return false
+	}
+
+	for i, v := range components {
+		if f.packageComponents[i] != v {
+			return false
+		}
+	}
+
+	return true
+}
+
+func (f *fqName) pkg() string {
+	return strings.Join(f.packageComponents, ".")
+}
+func (f *fqName) version() string {
+	return f.major + "." + f.minor
+}
+func (f *fqName) sanitizedVersion() string {
+	return "V" + f.major + "_" + f.minor
+}
+func (f *fqName) string() string {
+	return f.pkg() + "@" + f.version()
+}
+func (f *fqName) sanitizedString() string {
+	return f.pkg() + "-V" + f.version() // NOT sanitizedVersion
+}
+
+func (f *fqName) dir() string {
+	return filepath.Join(filepath.Join(f.packageComponents...), f.version()) + "/"
+}
+func (f *fqName) sanitizedDir() string {
+	return filepath.Join(filepath.Join(f.packageComponents...), f.sanitizedVersion()) + "/"
+}
+
+func (f *fqName) fileGroupName() string {
+	return f.string() + "_hal"
+}
+func (f *fqName) sourcesName() string {
+	return f.string() + "_genc++"
+}
+func (f *fqName) headersName() string {
+	return f.string() + "_genc++_headers"
+}
+func (f *fqName) javaName() string {
+	return f.sanitizedString() + "-java"
+}
+func (f *fqName) javaSourcesName() string {
+	return f.sanitizedString() + "-java_gen_java"
+}
+func (f *fqName) javaConstantsName() string {
+	return f.sanitizedString() + "-java-constants"
+}
+func (f *fqName) javaConstantsSourcesName() string {
+	return f.sanitizedString() + "-java-constants_gen_java"
+}
+func (f *fqName) adapterName() string {
+	return f.string() + "-adapter"
+}
+func (f *fqName) adapterSourcesName() string {
+	return f.string() + "-adapter_genc++"
+}
+func (f *fqName) adapterHelperName() string {
+	return f.string() + "-adapter-helper"
+}
+func (f *fqName) adapterHelperSourcesName() string {
+	return f.string() + "-adapter-helper_genc++"
+}
+func (f *fqName) adapterHelperHeadersName() string {
+	return f.string() + "-adapter-helper_genc++_headers"
+}
diff --git a/build/hidl_interface.go b/build/hidl_interface.go
new file mode 100644
index 0000000..c444c80
--- /dev/null
+++ b/build/hidl_interface.go
@@ -0,0 +1,386 @@
+// Copyright (C) 2017 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 hidl
+
+import (
+	"strings"
+	"sync"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/genrule"
+	"android/soong/java"
+)
+
+var (
+	hidlInterfaceSuffix = "_interface"
+)
+
+func init() {
+	android.RegisterModuleType("hidl_interface", hidlInterfaceFactory)
+}
+
+type hidlInterfaceProperties struct {
+	// Vndk properties for interface library only.
+	cc.VndkProperties
+
+	// List of .hal files which compose this interface.
+	Srcs []string
+
+	// List of hal interface packages that this library depends on.
+	Interfaces []string
+
+	// Package root for this package, must be a prefix of name
+	Root string
+
+	// List of non-TypeDef types declared in types.hal.
+	Types []string
+
+	// Whether to generate the Java library stubs.
+	// Default: true
+	Gen_java *bool
+
+	// Whether to generate a Java library containing constants
+	// expressed by @export annotations in the hal files.
+	Gen_java_constants bool
+
+	// Don't generate "android.hidl.foo@1.0" C library. Instead
+	// only generate the genrules so that this package can be
+	// included in libhidltransport.
+	Core_interface bool
+}
+
+type hidlInterface struct {
+	android.ModuleBase
+
+	properties hidlInterfaceProperties
+}
+
+var _ genrule.SourceFileGenerator = (*hidlInterface)(nil)
+
+func processSources(mctx android.LoadHookContext, srcs []string) ([]string, []string, bool) {
+	var interfaces []string
+	var types []string // hidl-gen only supports types.hal, but don't assume that here
+
+	hasError := false
+
+	for _, v := range srcs {
+		if !strings.HasSuffix(v, ".hal") {
+			mctx.PropertyErrorf("srcs", "Source must be a .hal file: "+v)
+			hasError = true
+			continue
+		}
+
+		name := strings.TrimSuffix(v, ".hal")
+
+		if strings.HasPrefix(name, "I") {
+			baseName := strings.TrimPrefix(name, "I")
+			interfaces = append(interfaces, baseName)
+		} else {
+			types = append(types, name)
+		}
+	}
+
+	return interfaces, types, !hasError
+}
+
+func processDependencies(mctx android.LoadHookContext, interfaces []string) ([]string, []string, bool) {
+	var dependencies []string
+	var javaDependencies []string
+
+	hasError := false
+
+	for _, v := range interfaces {
+		name, err := parseFqName(v)
+		if err != nil {
+			mctx.PropertyErrorf("interfaces", err.Error())
+			hasError = true
+			continue
+		}
+		dependencies = append(dependencies, name.string())
+		javaDependencies = append(javaDependencies, name.javaName())
+	}
+
+	return dependencies, javaDependencies, !hasError
+}
+
+func getRootList(mctx android.LoadHookContext, interfaces []string) ([]string, bool) {
+	var roots []string
+	hasError := false
+
+	for _, i := range interfaces {
+		interfaceObject := lookupInterface(i)
+		if interfaceObject == nil {
+			mctx.PropertyErrorf("interfaces", "Cannot find interface "+i)
+			hasError = true
+			continue
+		}
+		root := interfaceObject.properties.Root
+		rootObject := lookupPackageRoot(root)
+		if rootObject == nil {
+			mctx.PropertyErrorf("interfaces", "Cannot find package root for "+i+" which is '"+root+"'")
+			hasError = true
+			continue
+		}
+
+		roots = append(roots, root+":"+rootObject.properties.Path)
+	}
+
+	return android.FirstUniqueStrings(roots), !hasError
+}
+
+func removeCoreDependencies(mctx android.LoadHookContext, dependencies []string) ([]string, bool) {
+	var ret []string
+	hasError := false
+
+	for _, i := range dependencies {
+		interfaceObject := lookupInterface(i)
+		if interfaceObject == nil {
+			mctx.PropertyErrorf("interfaces", "Cannot find interface "+i)
+			hasError = true
+			continue
+		}
+
+		if !interfaceObject.properties.Core_interface {
+			ret = append(ret, i)
+		}
+	}
+
+	return ret, !hasError
+}
+
+func hidlGenCommand(lang string, roots []string, name *fqName) *string {
+	cmd := "$(location hidl-gen) -o $(genDir)"
+	cmd += " -L" + lang
+	cmd += " " + strings.Join(wrap("-r", roots, ""), " ")
+	cmd += " " + name.string()
+	return &cmd
+}
+
+func hidlInterfaceMutator(mctx android.LoadHookContext, i *hidlInterface) {
+	name, err := parseFqName(i.ModuleBase.Name())
+	if err != nil {
+		mctx.PropertyErrorf("name", err.Error())
+	}
+
+	if !name.inPackage(i.properties.Root) {
+		mctx.PropertyErrorf("root", "Root, "+i.properties.Root+", for "+name.string()+" must be a prefix.")
+	}
+
+	interfaces, types, _ := processSources(mctx, i.properties.Srcs)
+
+	if len(interfaces) == 0 && len(types) == 0 {
+		mctx.PropertyErrorf("srcs", "No sources provided.")
+	}
+
+	dependencies, javaDependencies, _ := processDependencies(mctx, i.properties.Interfaces)
+	roots, _ := getRootList(mctx, dependencies)
+	cppDependencies, _ := removeCoreDependencies(mctx, dependencies)
+
+	if mctx.Failed() {
+		return
+	}
+
+	shouldGenerateLibrary := !i.properties.Core_interface
+	// explicitly true if not specified to give early warning to devs
+	shouldGenerateJava := i.properties.Gen_java == nil || *i.properties.Gen_java
+	shouldGenerateJavaConstants := i.properties.Gen_java_constants
+
+	var libraryIfExists []string
+	if shouldGenerateLibrary {
+		libraryIfExists = []string{name.string()}
+	}
+
+	// TODO(b/69002743): remove filegroups
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.FileGroupFactory), &fileGroupProperties{
+		Name: proptools.StringPtr(name.fileGroupName()),
+		Srcs: i.properties.Srcs,
+	})
+
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:  proptools.StringPtr(name.sourcesName()),
+		Tools: []string{"hidl-gen"},
+		Cmd:   hidlGenCommand("c++-sources", roots, name),
+		Srcs:  i.properties.Srcs,
+		Out: concat(wrap(name.dir(), interfaces, "All.cpp"),
+			wrap(name.dir(), types, ".cpp")),
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:  proptools.StringPtr(name.headersName()),
+		Tools: []string{"hidl-gen"},
+		Cmd:   hidlGenCommand("c++-headers", roots, name),
+		Srcs:  i.properties.Srcs,
+		Out: concat(wrap(name.dir()+"I", interfaces, ".h"),
+			wrap(name.dir()+"Bs", interfaces, ".h"),
+			wrap(name.dir()+"BnHw", interfaces, ".h"),
+			wrap(name.dir()+"BpHw", interfaces, ".h"),
+			wrap(name.dir()+"IHw", interfaces, ".h"),
+			wrap(name.dir(), types, ".h"),
+			wrap(name.dir()+"hw", types, ".h")),
+	})
+
+	if shouldGenerateLibrary {
+		mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
+			Name:              proptools.StringPtr(name.string()),
+			Vendor_available:  proptools.BoolPtr(true),
+			Defaults:          []string{"hidl-module-defaults"},
+			Generated_sources: []string{name.sourcesName()},
+			Generated_headers: []string{name.headersName()},
+			Shared_libs: concat(cppDependencies, []string{
+				"libhidlbase",
+				"libhidltransport",
+				"libhwbinder",
+				"liblog",
+				"libutils",
+				"libcutils",
+			}),
+			Export_shared_lib_headers: concat(cppDependencies, []string{
+				"libhidlbase",
+				"libhidltransport",
+				"libhwbinder",
+				"libutils",
+			}),
+			Export_generated_headers: []string{name.headersName()},
+		}, &i.properties.VndkProperties)
+	}
+
+	if shouldGenerateJava {
+		mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+			Name:  proptools.StringPtr(name.javaSourcesName()),
+			Tools: []string{"hidl-gen"},
+			Cmd:   hidlGenCommand("java", roots, name),
+			Srcs:  i.properties.Srcs,
+			Out: concat(wrap(name.sanitizedDir()+"I", interfaces, ".java"),
+				wrap(name.sanitizedDir(), i.properties.Types, ".java")),
+		})
+		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+			Name:              proptools.StringPtr(name.javaName()),
+			Defaults:          []string{"hidl-java-module-defaults"},
+			No_framework_libs: proptools.BoolPtr(true),
+			Srcs:              []string{":" + name.javaSourcesName()},
+			Libs:              append(javaDependencies, "hwbinder"),
+		})
+	}
+
+	if shouldGenerateJavaConstants {
+		mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+			Name:  proptools.StringPtr(name.javaConstantsSourcesName()),
+			Tools: []string{"hidl-gen"},
+			Cmd:   hidlGenCommand("java-constants", roots, name),
+			Srcs:  i.properties.Srcs,
+			Out:   []string{name.sanitizedDir() + "Constants.java"},
+		})
+		mctx.CreateModule(android.ModuleFactoryAdaptor(java.LibraryFactory(true)), &javaProperties{
+			Name:              proptools.StringPtr(name.javaConstantsName()),
+			Defaults:          []string{"hidl-java-module-defaults"},
+			No_framework_libs: proptools.BoolPtr(true),
+			Srcs:              []string{":" + name.javaConstantsSourcesName()},
+		})
+	}
+
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:  proptools.StringPtr(name.adapterHelperSourcesName()),
+		Tools: []string{"hidl-gen"},
+		Cmd:   hidlGenCommand("c++-adapter-sources", roots, name),
+		Srcs:  i.properties.Srcs,
+		Out:   wrap(name.dir()+"A", concat(interfaces, types), ".cpp"),
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:  proptools.StringPtr(name.adapterHelperHeadersName()),
+		Tools: []string{"hidl-gen"},
+		Cmd:   hidlGenCommand("c++-adapter-headers", roots, name),
+		Srcs:  i.properties.Srcs,
+		Out:   wrap(name.dir()+"A", concat(interfaces, types), ".h"),
+	})
+
+	mctx.CreateModule(android.ModuleFactoryAdaptor(cc.LibraryFactory), &ccProperties{
+		Name:              proptools.StringPtr(name.adapterHelperName()),
+		Vendor_available:  proptools.BoolPtr(true),
+		Defaults:          []string{"hidl-module-defaults"},
+		Generated_sources: []string{name.adapterHelperSourcesName()},
+		Generated_headers: []string{name.adapterHelperHeadersName()},
+		Shared_libs: concat([]string{
+			"libhidlbase",
+			"libhidltransport",
+			"libutils",
+			"libhidladapter",
+		}, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+		Export_shared_lib_headers: concat([]string{
+			"libhidlbase",
+			"libhidltransport",
+			"libhidladapter",
+		}, wrap("", dependencies, "-adapter-helper"), cppDependencies, libraryIfExists),
+		Export_generated_headers: []string{name.adapterHelperHeadersName()},
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(genrule.GenRuleFactory), &genruleProperties{
+		Name:  proptools.StringPtr(name.adapterSourcesName()),
+		Tools: []string{"hidl-gen"},
+		Cmd:   hidlGenCommand("c++-adapter-main", roots, name),
+		Srcs:  i.properties.Srcs,
+		Out:   []string{"main.cpp"},
+	})
+	mctx.CreateModule(android.ModuleFactoryAdaptor(cc.TestFactory), &ccProperties{
+		Name:              proptools.StringPtr(name.adapterName()),
+		Generated_sources: []string{name.adapterSourcesName()},
+		Shared_libs: concat([]string{
+			"libhidladapter",
+			"libhidlbase",
+			"libhidltransport",
+			"libutils",
+			name.adapterHelperName(),
+		}, libraryIfExists),
+	})
+}
+
+func (h *hidlInterface) Name() string {
+	return h.ModuleBase.Name() + hidlInterfaceSuffix
+}
+func (h *hidlInterface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+func (h *hidlInterface) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+func (h *hidlInterface) GeneratedHeaderDirs() android.Paths {
+	return []android.Path{}
+}
+func (h *hidlInterface) GeneratedSourceFiles() android.Paths {
+	return []android.Path{}
+}
+
+var hidlInterfaceMutex sync.Mutex
+var hidlInterfaces []*hidlInterface
+
+func hidlInterfaceFactory() android.Module {
+	i := &hidlInterface{}
+	i.AddProperties(&i.properties)
+	android.InitAndroidModule(i)
+	android.AddLoadHook(i, func(ctx android.LoadHookContext) { hidlInterfaceMutator(ctx, i) })
+
+	hidlInterfaceMutex.Lock()
+	hidlInterfaces = append(hidlInterfaces, i)
+	hidlInterfaceMutex.Unlock()
+
+	return i
+}
+
+func lookupInterface(name string) *hidlInterface {
+	for _, i := range hidlInterfaces {
+		if i.ModuleBase.Name() == name {
+			return i
+		}
+	}
+	return nil
+}
diff --git a/build/hidl_package_root.go b/build/hidl_package_root.go
new file mode 100644
index 0000000..15c63f3
--- /dev/null
+++ b/build/hidl_package_root.go
@@ -0,0 +1,72 @@
+// Copyright (C) 2017 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 hidl
+
+import (
+	"sync"
+
+	"android/soong/android"
+	"android/soong/genrule"
+)
+
+func init() {
+	android.RegisterModuleType("hidl_package_root", hidlPackageRootFactory)
+}
+
+type hidlPackageRoot struct {
+	android.ModuleBase
+
+	properties struct {
+		// path to this module from root
+		Path string
+	}
+}
+
+var _ genrule.SourceFileGenerator = (*hidlPackageRoot)(nil)
+
+func (r *hidlPackageRoot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+func (r *hidlPackageRoot) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+func (r *hidlPackageRoot) GeneratedHeaderDirs() android.Paths {
+	return []android.Path{}
+}
+func (r *hidlPackageRoot) GeneratedSourceFiles() android.Paths {
+	return []android.Path{}
+}
+
+var packageRootsMutex sync.Mutex
+var packageRoots []*hidlPackageRoot
+
+func hidlPackageRootFactory() android.Module {
+	r := &hidlPackageRoot{}
+	r.AddProperties(&r.properties)
+	android.InitAndroidModule(r)
+
+	packageRootsMutex.Lock()
+	packageRoots = append(packageRoots, r)
+	packageRootsMutex.Unlock()
+
+	return r
+}
+
+func lookupPackageRoot(name string) *hidlPackageRoot {
+	for _, i := range packageRoots {
+		if i.ModuleBase.Name() == name {
+			return i
+		}
+	}
+	return nil
+}
diff --git a/build/properties.go b/build/properties.go
new file mode 100644
index 0000000..2ae38be
--- /dev/null
+++ b/build/properties.go
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 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 hidl
+
+type nameProperties struct {
+	Name *string
+}
+
+type fileGroupProperties struct {
+	Name *string
+	Srcs []string
+}
+
+type genruleProperties struct {
+	Name  *string
+	Tools []string
+	Cmd   *string
+	Srcs  []string
+	Out   []string
+}
+
+type ccProperties struct {
+	Name                      *string
+	Defaults                  []string
+	Vendor_available          *bool
+	Generated_sources         []string
+	Generated_headers         []string
+	Shared_libs               []string
+	Export_shared_lib_headers []string
+	Export_generated_headers  []string
+}
+
+type javaProperties struct {
+	Name              *string
+	Defaults          []string
+	No_framework_libs *bool
+	Srcs              []string
+	Libs              []string
+}
diff --git a/build/utils.go b/build/utils.go
new file mode 100644
index 0000000..ba3ac8e
--- /dev/null
+++ b/build/utils.go
@@ -0,0 +1,43 @@
+// Copyright (C) 2017 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 hidl
+
+// wrap(p, a, s) = [p + v + s for v in a]
+func wrap(prefix string, strs []string, suffix string) []string {
+	ret := make([]string, len(strs))
+	for i, v := range strs {
+		ret[i] = prefix + v + suffix
+	}
+	return ret
+}
+
+// concat(a...) = sum((i for i in a), [])
+func concat(sstrs ...[]string) []string {
+	var ret []string
+	for _, v := range sstrs {
+		ret = append(ret, v...)
+	}
+	return ret
+}
+
+func remove(str string, strs []string) []string {
+	var ret []string
+	for _, v := range strs {
+		if v != str {
+			ret = append(ret, v)
+		}
+	}
+	return ret
+}