project_json.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // 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. package rust
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "path"
  19. "android/soong/android"
  20. )
  21. // This singleton collects Rust crate definitions and generates a JSON file
  22. // (${OUT_DIR}/soong/rust-project.json) which can be use by external tools,
  23. // such as rust-analyzer. It does so when either make, mm, mma, mmm or mmma is
  24. // called. This singleton is enabled only if SOONG_GEN_RUST_PROJECT is set.
  25. // For example,
  26. //
  27. // $ SOONG_GEN_RUST_PROJECT=1 m nothing
  28. const (
  29. // Environment variables used to control the behavior of this singleton.
  30. envVariableCollectRustDeps = "SOONG_GEN_RUST_PROJECT"
  31. rustProjectJsonFileName = "rust-project.json"
  32. )
  33. // The format of rust-project.json is not yet finalized. A current description is available at:
  34. // https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc#non-cargo-based-projects
  35. type rustProjectDep struct {
  36. // The Crate attribute is the index of the dependency in the Crates array in rustProjectJson.
  37. Crate int `json:"crate"`
  38. Name string `json:"name"`
  39. }
  40. type rustProjectCrate struct {
  41. DisplayName string `json:"display_name"`
  42. RootModule string `json:"root_module"`
  43. Edition string `json:"edition,omitempty"`
  44. Deps []rustProjectDep `json:"deps"`
  45. Cfg []string `json:"cfg"`
  46. Env map[string]string `json:"env"`
  47. ProcMacro bool `json:"is_proc_macro"`
  48. }
  49. type rustProjectJson struct {
  50. Crates []rustProjectCrate `json:"crates"`
  51. }
  52. // crateInfo is used during the processing to keep track of the known crates.
  53. type crateInfo struct {
  54. Idx int // Index of the crate in rustProjectJson.Crates slice.
  55. Deps map[string]int // The keys are the module names and not the crate names.
  56. }
  57. type projectGeneratorSingleton struct {
  58. project rustProjectJson
  59. knownCrates map[string]crateInfo // Keys are module names.
  60. }
  61. func rustProjectGeneratorSingleton() android.Singleton {
  62. return &projectGeneratorSingleton{}
  63. }
  64. func init() {
  65. android.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
  66. }
  67. // sourceProviderVariantSource returns the path to the source file if this
  68. // module variant should be used as a priority.
  69. //
  70. // SourceProvider modules may have multiple variants considered as source
  71. // (e.g., x86_64 and armv8). For a module available on device, use the source
  72. // generated for the target. For a host-only module, use the source generated
  73. // for the host.
  74. func sourceProviderVariantSource(ctx android.SingletonContext, rModule *Module) (string, bool) {
  75. rustLib, ok := rModule.compiler.(*libraryDecorator)
  76. if !ok {
  77. return "", false
  78. }
  79. if rustLib.source() {
  80. switch rModule.hod {
  81. case android.HostSupported, android.HostSupportedNoCross:
  82. if rModule.Target().String() == ctx.Config().BuildOSTarget.String() {
  83. src := rustLib.sourceProvider.Srcs()[0]
  84. return src.String(), true
  85. }
  86. default:
  87. if rModule.Target().String() == ctx.Config().AndroidFirstDeviceTarget.String() {
  88. src := rustLib.sourceProvider.Srcs()[0]
  89. return src.String(), true
  90. }
  91. }
  92. }
  93. return "", false
  94. }
  95. // sourceProviderSource finds the main source file of a source-provider crate.
  96. func sourceProviderSource(ctx android.SingletonContext, rModule *Module) (string, bool) {
  97. rustLib, ok := rModule.compiler.(*libraryDecorator)
  98. if !ok {
  99. return "", false
  100. }
  101. if rustLib.source() {
  102. // This is a source-variant, check if we are the right variant
  103. // depending on the module configuration.
  104. if src, ok := sourceProviderVariantSource(ctx, rModule); ok {
  105. return src, true
  106. }
  107. }
  108. foundSource := false
  109. sourceSrc := ""
  110. // Find the variant with the source and return its.
  111. ctx.VisitAllModuleVariants(rModule, func(variant android.Module) {
  112. if foundSource {
  113. return
  114. }
  115. // All variants of a source provider library are libraries.
  116. rVariant, _ := variant.(*Module)
  117. variantLib, _ := rVariant.compiler.(*libraryDecorator)
  118. if variantLib.source() {
  119. sourceSrc, ok = sourceProviderVariantSource(ctx, rVariant)
  120. if ok {
  121. foundSource = true
  122. }
  123. }
  124. })
  125. if !foundSource {
  126. ctx.Errorf("No valid source for source provider found: %v\n", rModule)
  127. }
  128. return sourceSrc, foundSource
  129. }
  130. // crateSource finds the main source file (.rs) for a crate.
  131. func crateSource(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (string, bool) {
  132. // Basic libraries, executables and tests.
  133. srcs := comp.Properties.Srcs
  134. if len(srcs) != 0 {
  135. return path.Join(ctx.ModuleDir(rModule), srcs[0]), true
  136. }
  137. // SourceProvider libraries.
  138. if rModule.sourceProvider != nil {
  139. return sourceProviderSource(ctx, rModule)
  140. }
  141. return "", false
  142. }
  143. // mergeDependencies visits all the dependencies for module and updates crate and deps
  144. // with any new dependency.
  145. func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext,
  146. module *Module, crate *rustProjectCrate, deps map[string]int) {
  147. ctx.VisitDirectDeps(module, func(child android.Module) {
  148. // Skip intra-module dependencies (i.e., generated-source library depending on the source variant).
  149. if module.Name() == child.Name() {
  150. return
  151. }
  152. // Skip unsupported modules.
  153. rChild, compChild, ok := isModuleSupported(ctx, child)
  154. if !ok {
  155. return
  156. }
  157. // For unknown dependency, add it first.
  158. var childId int
  159. cInfo, known := singleton.knownCrates[rChild.Name()]
  160. if !known {
  161. childId, ok = singleton.addCrate(ctx, rChild, compChild)
  162. if !ok {
  163. return
  164. }
  165. } else {
  166. childId = cInfo.Idx
  167. }
  168. // Is this dependency known already?
  169. if _, ok = deps[child.Name()]; ok {
  170. return
  171. }
  172. crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: rChild.CrateName()})
  173. deps[child.Name()] = childId
  174. })
  175. }
  176. // isModuleSupported returns the RustModule and baseCompiler if the module
  177. // should be considered for inclusion in rust-project.json.
  178. func isModuleSupported(ctx android.SingletonContext, module android.Module) (*Module, *baseCompiler, bool) {
  179. rModule, ok := module.(*Module)
  180. if !ok {
  181. return nil, nil, false
  182. }
  183. if rModule.compiler == nil {
  184. return nil, nil, false
  185. }
  186. var comp *baseCompiler
  187. switch c := rModule.compiler.(type) {
  188. case *libraryDecorator:
  189. comp = c.baseCompiler
  190. case *binaryDecorator:
  191. comp = c.baseCompiler
  192. case *testDecorator:
  193. comp = c.binaryDecorator.baseCompiler
  194. case *procMacroDecorator:
  195. comp = c.baseCompiler
  196. case *toolchainLibraryDecorator:
  197. comp = c.baseCompiler
  198. default:
  199. return nil, nil, false
  200. }
  201. return rModule, comp, true
  202. }
  203. // addCrate adds a crate to singleton.project.Crates ensuring that required
  204. // dependencies are also added. It returns the index of the new crate in
  205. // singleton.project.Crates
  206. func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (int, bool) {
  207. rootModule, ok := crateSource(ctx, rModule, comp)
  208. if !ok {
  209. ctx.Errorf("Unable to find source for valid module: %v", rModule)
  210. return 0, false
  211. }
  212. _, procMacro := rModule.compiler.(*procMacroDecorator)
  213. crate := rustProjectCrate{
  214. DisplayName: rModule.Name(),
  215. RootModule: rootModule,
  216. Edition: comp.edition(),
  217. Deps: make([]rustProjectDep, 0),
  218. Cfg: make([]string, 0),
  219. Env: make(map[string]string),
  220. ProcMacro: procMacro,
  221. }
  222. if comp.CargoOutDir().Valid() {
  223. crate.Env["OUT_DIR"] = comp.CargoOutDir().String()
  224. }
  225. for _, feature := range comp.Properties.Features {
  226. crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"")
  227. }
  228. deps := make(map[string]int)
  229. singleton.mergeDependencies(ctx, rModule, &crate, deps)
  230. idx := len(singleton.project.Crates)
  231. singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps}
  232. singleton.project.Crates = append(singleton.project.Crates, crate)
  233. return idx, true
  234. }
  235. // appendCrateAndDependencies creates a rustProjectCrate for the module argument and appends it to singleton.project.
  236. // It visits the dependencies of the module depth-first so the dependency ID can be added to the current module. If the
  237. // current module is already in singleton.knownCrates, its dependencies are merged.
  238. func (singleton *projectGeneratorSingleton) appendCrateAndDependencies(ctx android.SingletonContext, module android.Module) {
  239. rModule, comp, ok := isModuleSupported(ctx, module)
  240. if !ok {
  241. return
  242. }
  243. // If we have seen this crate already; merge any new dependencies.
  244. if cInfo, ok := singleton.knownCrates[module.Name()]; ok {
  245. crate := singleton.project.Crates[cInfo.Idx]
  246. singleton.mergeDependencies(ctx, rModule, &crate, cInfo.Deps)
  247. singleton.project.Crates[cInfo.Idx] = crate
  248. return
  249. }
  250. singleton.addCrate(ctx, rModule, comp)
  251. }
  252. func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
  253. if !ctx.Config().IsEnvTrue(envVariableCollectRustDeps) {
  254. return
  255. }
  256. singleton.knownCrates = make(map[string]crateInfo)
  257. ctx.VisitAllModules(func(module android.Module) {
  258. singleton.appendCrateAndDependencies(ctx, module)
  259. })
  260. path := android.PathForOutput(ctx, rustProjectJsonFileName)
  261. err := createJsonFile(singleton.project, path)
  262. if err != nil {
  263. ctx.Errorf(err.Error())
  264. }
  265. }
  266. func createJsonFile(project rustProjectJson, rustProjectPath android.WritablePath) error {
  267. buf, err := json.MarshalIndent(project, "", " ")
  268. if err != nil {
  269. return fmt.Errorf("JSON marshal of rustProjectJson failed: %s", err)
  270. }
  271. err = android.WriteFileToOutputDir(rustProjectPath, buf, 0666)
  272. if err != nil {
  273. return fmt.Errorf("Writing rust-project to %s failed: %s", rustProjectPath.String(), err)
  274. }
  275. return nil
  276. }