blob: e37c163d4f594696f8bc246bee4a9dba86232073 [file] [log] [blame]
Jingwen Chen5ba7e472020-07-15 10:06:41 +00001// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18 "android/soong/android"
19 "fmt"
20 "io/ioutil"
21 "os"
22 "path/filepath"
23 "strings"
24
25 "github.com/google/blueprint"
26)
27
28const (
29 soongModuleLoad = `package(default_visibility = ["//visibility:public"])
30load("//:soong_module.bzl", "soong_module")
31`
32
33 // A BUILD file target snippet representing a Soong module
34 soongModuleTarget = `soong_module(
35 name = "%s",
36 module_name = "%s",
37 module_type = "%s",
38 module_variant = "%s",
39 deps = [
40 %s
41 ],
42)
43`
44
45 // The soong_module rule implementation in a .bzl file
46 soongModuleBzl = `
47SoongModuleInfo = provider(
48 fields = {
49 "name": "Name of module",
50 "type": "Type of module",
51 "variant": "Variant of module",
52 },
53)
54
55def _soong_module_impl(ctx):
56 return [
57 SoongModuleInfo(
58 name = ctx.attr.module_name,
59 type = ctx.attr.module_type,
60 variant = ctx.attr.module_variant,
61 ),
62 ]
63
64soong_module = rule(
65 implementation = _soong_module_impl,
66 attrs = {
67 "module_name": attr.string(mandatory = True),
68 "module_type": attr.string(mandatory = True),
69 "module_variant": attr.string(),
70 "deps": attr.label_list(providers = [SoongModuleInfo]),
71 },
72)
73`
74)
75
76func targetNameWithVariant(c *blueprint.Context, logicModule blueprint.Module) string {
77 name := ""
78 if c.ModuleSubDir(logicModule) != "" {
79 name = c.ModuleName(logicModule) + "--" + c.ModuleSubDir(logicModule)
80 } else {
81 name = c.ModuleName(logicModule)
82 }
83
84 return strings.Replace(name, "//", "", 1)
85}
86
87func qualifiedTargetLabel(c *blueprint.Context, logicModule blueprint.Module) string {
88 return "//" +
89 packagePath(c, logicModule) +
90 ":" +
91 targetNameWithVariant(c, logicModule)
92}
93
94func packagePath(c *blueprint.Context, logicModule blueprint.Module) string {
95 return filepath.Dir(c.BlueprintFile(logicModule))
96}
97
98func createBazelOverlay(ctx *android.Context, bazelOverlayDir string) error {
99 blueprintCtx := ctx.Context
100 blueprintCtx.VisitAllModules(func(module blueprint.Module) {
101 buildFile, err := buildFileForModule(blueprintCtx, module)
102 if err != nil {
103 panic(err)
104 }
105
106 // TODO(b/163018919): DirectDeps can have duplicate (module, variant)
107 // items, if the modules are added using different DependencyTag. Figure
108 // out the implications of that.
109 depLabels := map[string]bool{}
110 blueprintCtx.VisitDirectDeps(module, func(depModule blueprint.Module) {
111 depLabels[qualifiedTargetLabel(blueprintCtx, depModule)] = true
112 })
113
114 var depLabelList string
115 for depLabel, _ := range depLabels {
116 depLabelList += "\"" + depLabel + "\",\n "
117 }
118 buildFile.Write([]byte(
119 fmt.Sprintf(
120 soongModuleTarget,
121 targetNameWithVariant(blueprintCtx, module),
122 blueprintCtx.ModuleName(module),
123 blueprintCtx.ModuleType(module),
124 // misleading name, this actually returns the variant.
125 blueprintCtx.ModuleSubDir(module),
126 depLabelList)))
127 buildFile.Close()
128 })
129
130 if err := writeReadOnlyFile(bazelOverlayDir, "WORKSPACE", ""); err != nil {
131 return err
132 }
133
134 if err := writeReadOnlyFile(bazelOverlayDir, "BUILD", ""); err != nil {
135 return err
136 }
137
138 return writeReadOnlyFile(bazelOverlayDir, "soong_module.bzl", soongModuleBzl)
139}
140
141func buildFileForModule(ctx *blueprint.Context, module blueprint.Module) (*os.File, error) {
142 // Create nested directories for the BUILD file
143 dirPath := filepath.Join(bazelOverlayDir, packagePath(ctx, module))
144 if _, err := os.Stat(dirPath); os.IsNotExist(err) {
145 os.MkdirAll(dirPath, os.ModePerm)
146 }
147 // Open the file for appending, and create it if it doesn't exist
148 f, err := os.OpenFile(
149 filepath.Join(dirPath, "BUILD.bazel"),
150 os.O_APPEND|os.O_CREATE|os.O_WRONLY,
151 0644)
152 if err != nil {
153 return nil, err
154 }
155
156 // If the file is empty, add the load statement for the `soong_module` rule
157 fi, err := f.Stat()
158 if err != nil {
159 return nil, err
160 }
161 if fi.Size() == 0 {
162 f.Write([]byte(soongModuleLoad + "\n"))
163 }
164
165 return f, nil
166}
167
168// The overlay directory should be read-only, sufficient for bazel query.
169func writeReadOnlyFile(dir string, baseName string, content string) error {
170 workspaceFile := filepath.Join(bazelOverlayDir, baseName)
171 // 0444 is read-only
172 return ioutil.WriteFile(workspaceFile, []byte(content), 0444)
173}