1*333d2b36SAndroid Build Coastguard Worker// Copyright 2020 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage rust 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "encoding/json" 19*333d2b36SAndroid Build Coastguard Worker "fmt" 20*333d2b36SAndroid Build Coastguard Worker 21*333d2b36SAndroid Build Coastguard Worker "android/soong/android" 22*333d2b36SAndroid Build Coastguard Worker "android/soong/rust/config" 23*333d2b36SAndroid Build Coastguard Worker) 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Worker// This singleton collects Rust crate definitions and generates a JSON file 26*333d2b36SAndroid Build Coastguard Worker// (${OUT_DIR}/soong/rust-project.json) which can be use by external tools, 27*333d2b36SAndroid Build Coastguard Worker// such as rust-analyzer. It does so when either make, mm, mma, mmm or mmma is 28*333d2b36SAndroid Build Coastguard Worker// called. This singleton is enabled only if SOONG_GEN_RUST_PROJECT is set. 29*333d2b36SAndroid Build Coastguard Worker// For example, 30*333d2b36SAndroid Build Coastguard Worker// 31*333d2b36SAndroid Build Coastguard Worker// $ SOONG_GEN_RUST_PROJECT=1 m nothing 32*333d2b36SAndroid Build Coastguard Worker 33*333d2b36SAndroid Build Coastguard Workerconst ( 34*333d2b36SAndroid Build Coastguard Worker // Environment variables used to control the behavior of this singleton. 35*333d2b36SAndroid Build Coastguard Worker envVariableCollectRustDeps = "SOONG_GEN_RUST_PROJECT" 36*333d2b36SAndroid Build Coastguard Worker rustProjectJsonFileName = "rust-project.json" 37*333d2b36SAndroid Build Coastguard Worker) 38*333d2b36SAndroid Build Coastguard Worker 39*333d2b36SAndroid Build Coastguard Worker// The format of rust-project.json is not yet finalized. A current description is available at: 40*333d2b36SAndroid Build Coastguard Worker// https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc#non-cargo-based-projects 41*333d2b36SAndroid Build Coastguard Workertype rustProjectDep struct { 42*333d2b36SAndroid Build Coastguard Worker // The Crate attribute is the index of the dependency in the Crates array in rustProjectJson. 43*333d2b36SAndroid Build Coastguard Worker Crate int `json:"crate"` 44*333d2b36SAndroid Build Coastguard Worker Name string `json:"name"` 45*333d2b36SAndroid Build Coastguard Worker} 46*333d2b36SAndroid Build Coastguard Worker 47*333d2b36SAndroid Build Coastguard Workertype rustProjectCrate struct { 48*333d2b36SAndroid Build Coastguard Worker DisplayName string `json:"display_name"` 49*333d2b36SAndroid Build Coastguard Worker RootModule string `json:"root_module"` 50*333d2b36SAndroid Build Coastguard Worker Edition string `json:"edition,omitempty"` 51*333d2b36SAndroid Build Coastguard Worker Deps []rustProjectDep `json:"deps"` 52*333d2b36SAndroid Build Coastguard Worker Cfg []string `json:"cfg"` 53*333d2b36SAndroid Build Coastguard Worker Env map[string]string `json:"env"` 54*333d2b36SAndroid Build Coastguard Worker ProcMacro bool `json:"is_proc_macro"` 55*333d2b36SAndroid Build Coastguard Worker ProcMacroDylib *string `json:"proc_macro_dylib_path"` 56*333d2b36SAndroid Build Coastguard Worker} 57*333d2b36SAndroid Build Coastguard Worker 58*333d2b36SAndroid Build Coastguard Workertype rustProjectJson struct { 59*333d2b36SAndroid Build Coastguard Worker Sysroot string `json:"sysroot"` 60*333d2b36SAndroid Build Coastguard Worker Crates []rustProjectCrate `json:"crates"` 61*333d2b36SAndroid Build Coastguard Worker} 62*333d2b36SAndroid Build Coastguard Worker 63*333d2b36SAndroid Build Coastguard Worker// crateInfo is used during the processing to keep track of the known crates. 64*333d2b36SAndroid Build Coastguard Workertype crateInfo struct { 65*333d2b36SAndroid Build Coastguard Worker Idx int // Index of the crate in rustProjectJson.Crates slice. 66*333d2b36SAndroid Build Coastguard Worker Deps map[string]int // The keys are the module names and not the crate names. 67*333d2b36SAndroid Build Coastguard Worker Device bool // True if the crate at idx was a device crate 68*333d2b36SAndroid Build Coastguard Worker} 69*333d2b36SAndroid Build Coastguard Worker 70*333d2b36SAndroid Build Coastguard Workertype projectGeneratorSingleton struct { 71*333d2b36SAndroid Build Coastguard Worker project rustProjectJson 72*333d2b36SAndroid Build Coastguard Worker knownCrates map[string]crateInfo // Keys are module names. 73*333d2b36SAndroid Build Coastguard Worker} 74*333d2b36SAndroid Build Coastguard Worker 75*333d2b36SAndroid Build Coastguard Workerfunc rustProjectGeneratorSingleton() android.Singleton { 76*333d2b36SAndroid Build Coastguard Worker return &projectGeneratorSingleton{} 77*333d2b36SAndroid Build Coastguard Worker} 78*333d2b36SAndroid Build Coastguard Worker 79*333d2b36SAndroid Build Coastguard Workerfunc init() { 80*333d2b36SAndroid Build Coastguard Worker android.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton) 81*333d2b36SAndroid Build Coastguard Worker} 82*333d2b36SAndroid Build Coastguard Worker 83*333d2b36SAndroid Build Coastguard Worker// mergeDependencies visits all the dependencies for module and updates crate and deps 84*333d2b36SAndroid Build Coastguard Worker// with any new dependency. 85*333d2b36SAndroid Build Coastguard Workerfunc (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext, 86*333d2b36SAndroid Build Coastguard Worker module *Module, crate *rustProjectCrate, deps map[string]int) { 87*333d2b36SAndroid Build Coastguard Worker 88*333d2b36SAndroid Build Coastguard Worker ctx.VisitDirectDeps(module, func(child android.Module) { 89*333d2b36SAndroid Build Coastguard Worker // Skip intra-module dependencies (i.e., generated-source library depending on the source variant). 90*333d2b36SAndroid Build Coastguard Worker if module.Name() == child.Name() { 91*333d2b36SAndroid Build Coastguard Worker return 92*333d2b36SAndroid Build Coastguard Worker } 93*333d2b36SAndroid Build Coastguard Worker // Skip unsupported modules. 94*333d2b36SAndroid Build Coastguard Worker rChild, ok := isModuleSupported(ctx, child) 95*333d2b36SAndroid Build Coastguard Worker if !ok { 96*333d2b36SAndroid Build Coastguard Worker return 97*333d2b36SAndroid Build Coastguard Worker } 98*333d2b36SAndroid Build Coastguard Worker // For unknown dependency, add it first. 99*333d2b36SAndroid Build Coastguard Worker var childId int 100*333d2b36SAndroid Build Coastguard Worker cInfo, known := singleton.knownCrates[rChild.Name()] 101*333d2b36SAndroid Build Coastguard Worker if !known { 102*333d2b36SAndroid Build Coastguard Worker childId, ok = singleton.addCrate(ctx, rChild) 103*333d2b36SAndroid Build Coastguard Worker if !ok { 104*333d2b36SAndroid Build Coastguard Worker return 105*333d2b36SAndroid Build Coastguard Worker } 106*333d2b36SAndroid Build Coastguard Worker } else { 107*333d2b36SAndroid Build Coastguard Worker childId = cInfo.Idx 108*333d2b36SAndroid Build Coastguard Worker } 109*333d2b36SAndroid Build Coastguard Worker // Is this dependency known already? 110*333d2b36SAndroid Build Coastguard Worker if _, ok = deps[child.Name()]; ok { 111*333d2b36SAndroid Build Coastguard Worker return 112*333d2b36SAndroid Build Coastguard Worker } 113*333d2b36SAndroid Build Coastguard Worker crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: rChild.CrateName()}) 114*333d2b36SAndroid Build Coastguard Worker deps[child.Name()] = childId 115*333d2b36SAndroid Build Coastguard Worker }) 116*333d2b36SAndroid Build Coastguard Worker} 117*333d2b36SAndroid Build Coastguard Worker 118*333d2b36SAndroid Build Coastguard Worker// isModuleSupported returns the RustModule if the module 119*333d2b36SAndroid Build Coastguard Worker// should be considered for inclusion in rust-project.json. 120*333d2b36SAndroid Build Coastguard Workerfunc isModuleSupported(ctx android.SingletonContext, module android.Module) (*Module, bool) { 121*333d2b36SAndroid Build Coastguard Worker rModule, ok := module.(*Module) 122*333d2b36SAndroid Build Coastguard Worker if !ok { 123*333d2b36SAndroid Build Coastguard Worker return nil, false 124*333d2b36SAndroid Build Coastguard Worker } 125*333d2b36SAndroid Build Coastguard Worker if !rModule.Enabled(ctx) { 126*333d2b36SAndroid Build Coastguard Worker return nil, false 127*333d2b36SAndroid Build Coastguard Worker } 128*333d2b36SAndroid Build Coastguard Worker return rModule, true 129*333d2b36SAndroid Build Coastguard Worker} 130*333d2b36SAndroid Build Coastguard Worker 131*333d2b36SAndroid Build Coastguard Worker// addCrate adds a crate to singleton.project.Crates ensuring that required 132*333d2b36SAndroid Build Coastguard Worker// dependencies are also added. It returns the index of the new crate in 133*333d2b36SAndroid Build Coastguard Worker// singleton.project.Crates 134*333d2b36SAndroid Build Coastguard Workerfunc (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module) (int, bool) { 135*333d2b36SAndroid Build Coastguard Worker deps := make(map[string]int) 136*333d2b36SAndroid Build Coastguard Worker rootModule, err := rModule.compiler.checkedCrateRootPath() 137*333d2b36SAndroid Build Coastguard Worker if err != nil { 138*333d2b36SAndroid Build Coastguard Worker return 0, false 139*333d2b36SAndroid Build Coastguard Worker } 140*333d2b36SAndroid Build Coastguard Worker 141*333d2b36SAndroid Build Coastguard Worker var procMacroDylib *string = nil 142*333d2b36SAndroid Build Coastguard Worker if procDec, procMacro := rModule.compiler.(*procMacroDecorator); procMacro { 143*333d2b36SAndroid Build Coastguard Worker procMacroDylib = new(string) 144*333d2b36SAndroid Build Coastguard Worker *procMacroDylib = procDec.baseCompiler.unstrippedOutputFilePath().String() 145*333d2b36SAndroid Build Coastguard Worker } 146*333d2b36SAndroid Build Coastguard Worker 147*333d2b36SAndroid Build Coastguard Worker crate := rustProjectCrate{ 148*333d2b36SAndroid Build Coastguard Worker DisplayName: rModule.Name(), 149*333d2b36SAndroid Build Coastguard Worker RootModule: rootModule.String(), 150*333d2b36SAndroid Build Coastguard Worker Edition: rModule.compiler.edition(), 151*333d2b36SAndroid Build Coastguard Worker Deps: make([]rustProjectDep, 0), 152*333d2b36SAndroid Build Coastguard Worker Cfg: make([]string, 0), 153*333d2b36SAndroid Build Coastguard Worker Env: make(map[string]string), 154*333d2b36SAndroid Build Coastguard Worker ProcMacro: procMacroDylib != nil, 155*333d2b36SAndroid Build Coastguard Worker ProcMacroDylib: procMacroDylib, 156*333d2b36SAndroid Build Coastguard Worker } 157*333d2b36SAndroid Build Coastguard Worker 158*333d2b36SAndroid Build Coastguard Worker if rModule.compiler.cargoOutDir().Valid() { 159*333d2b36SAndroid Build Coastguard Worker crate.Env["OUT_DIR"] = rModule.compiler.cargoOutDir().String() 160*333d2b36SAndroid Build Coastguard Worker } 161*333d2b36SAndroid Build Coastguard Worker 162*333d2b36SAndroid Build Coastguard Worker for _, feature := range rModule.compiler.features(ctx, rModule) { 163*333d2b36SAndroid Build Coastguard Worker crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"") 164*333d2b36SAndroid Build Coastguard Worker } 165*333d2b36SAndroid Build Coastguard Worker 166*333d2b36SAndroid Build Coastguard Worker singleton.mergeDependencies(ctx, rModule, &crate, deps) 167*333d2b36SAndroid Build Coastguard Worker 168*333d2b36SAndroid Build Coastguard Worker var idx int 169*333d2b36SAndroid Build Coastguard Worker if cInfo, ok := singleton.knownCrates[rModule.Name()]; ok { 170*333d2b36SAndroid Build Coastguard Worker idx = cInfo.Idx 171*333d2b36SAndroid Build Coastguard Worker singleton.project.Crates[idx] = crate 172*333d2b36SAndroid Build Coastguard Worker } else { 173*333d2b36SAndroid Build Coastguard Worker idx = len(singleton.project.Crates) 174*333d2b36SAndroid Build Coastguard Worker singleton.project.Crates = append(singleton.project.Crates, crate) 175*333d2b36SAndroid Build Coastguard Worker } 176*333d2b36SAndroid Build Coastguard Worker singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps, Device: rModule.Device()} 177*333d2b36SAndroid Build Coastguard Worker return idx, true 178*333d2b36SAndroid Build Coastguard Worker} 179*333d2b36SAndroid Build Coastguard Worker 180*333d2b36SAndroid Build Coastguard Worker// appendCrateAndDependencies creates a rustProjectCrate for the module argument and appends it to singleton.project. 181*333d2b36SAndroid Build Coastguard Worker// It visits the dependencies of the module depth-first so the dependency ID can be added to the current module. If the 182*333d2b36SAndroid Build Coastguard Worker// current module is already in singleton.knownCrates, its dependencies are merged. 183*333d2b36SAndroid Build Coastguard Workerfunc (singleton *projectGeneratorSingleton) appendCrateAndDependencies(ctx android.SingletonContext, module android.Module) { 184*333d2b36SAndroid Build Coastguard Worker rModule, ok := isModuleSupported(ctx, module) 185*333d2b36SAndroid Build Coastguard Worker if !ok { 186*333d2b36SAndroid Build Coastguard Worker return 187*333d2b36SAndroid Build Coastguard Worker } 188*333d2b36SAndroid Build Coastguard Worker // If we have seen this crate already; merge any new dependencies. 189*333d2b36SAndroid Build Coastguard Worker if cInfo, ok := singleton.knownCrates[module.Name()]; ok { 190*333d2b36SAndroid Build Coastguard Worker // If we have a new device variant, override the old one 191*333d2b36SAndroid Build Coastguard Worker if !cInfo.Device && rModule.Device() { 192*333d2b36SAndroid Build Coastguard Worker singleton.addCrate(ctx, rModule) 193*333d2b36SAndroid Build Coastguard Worker return 194*333d2b36SAndroid Build Coastguard Worker } 195*333d2b36SAndroid Build Coastguard Worker crate := singleton.project.Crates[cInfo.Idx] 196*333d2b36SAndroid Build Coastguard Worker singleton.mergeDependencies(ctx, rModule, &crate, cInfo.Deps) 197*333d2b36SAndroid Build Coastguard Worker singleton.project.Crates[cInfo.Idx] = crate 198*333d2b36SAndroid Build Coastguard Worker return 199*333d2b36SAndroid Build Coastguard Worker } 200*333d2b36SAndroid Build Coastguard Worker singleton.addCrate(ctx, rModule) 201*333d2b36SAndroid Build Coastguard Worker} 202*333d2b36SAndroid Build Coastguard Worker 203*333d2b36SAndroid Build Coastguard Workerfunc (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { 204*333d2b36SAndroid Build Coastguard Worker if !ctx.Config().IsEnvTrue(envVariableCollectRustDeps) { 205*333d2b36SAndroid Build Coastguard Worker return 206*333d2b36SAndroid Build Coastguard Worker } 207*333d2b36SAndroid Build Coastguard Worker 208*333d2b36SAndroid Build Coastguard Worker singleton.project.Sysroot = config.RustPath(ctx) 209*333d2b36SAndroid Build Coastguard Worker 210*333d2b36SAndroid Build Coastguard Worker singleton.knownCrates = make(map[string]crateInfo) 211*333d2b36SAndroid Build Coastguard Worker ctx.VisitAllModules(func(module android.Module) { 212*333d2b36SAndroid Build Coastguard Worker singleton.appendCrateAndDependencies(ctx, module) 213*333d2b36SAndroid Build Coastguard Worker }) 214*333d2b36SAndroid Build Coastguard Worker 215*333d2b36SAndroid Build Coastguard Worker path := android.PathForOutput(ctx, rustProjectJsonFileName) 216*333d2b36SAndroid Build Coastguard Worker err := createJsonFile(singleton.project, path) 217*333d2b36SAndroid Build Coastguard Worker if err != nil { 218*333d2b36SAndroid Build Coastguard Worker ctx.Errorf(err.Error()) 219*333d2b36SAndroid Build Coastguard Worker } 220*333d2b36SAndroid Build Coastguard Worker} 221*333d2b36SAndroid Build Coastguard Worker 222*333d2b36SAndroid Build Coastguard Workerfunc createJsonFile(project rustProjectJson, rustProjectPath android.WritablePath) error { 223*333d2b36SAndroid Build Coastguard Worker buf, err := json.MarshalIndent(project, "", " ") 224*333d2b36SAndroid Build Coastguard Worker if err != nil { 225*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("JSON marshal of rustProjectJson failed: %s", err) 226*333d2b36SAndroid Build Coastguard Worker } 227*333d2b36SAndroid Build Coastguard Worker err = android.WriteFileToOutputDir(rustProjectPath, buf, 0666) 228*333d2b36SAndroid Build Coastguard Worker if err != nil { 229*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("Writing rust-project to %s failed: %s", rustProjectPath.String(), err) 230*333d2b36SAndroid Build Coastguard Worker } 231*333d2b36SAndroid Build Coastguard Worker return nil 232*333d2b36SAndroid Build Coastguard Worker} 233