1// Copyright 2019 The Android Open Source Project
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 rust
16
17import (
18	"strings"
19
20	"github.com/google/blueprint"
21	"github.com/google/blueprint/pathtools"
22
23	"android/soong/android"
24	"android/soong/cc"
25)
26
27var (
28	_     = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
29	rustc = pctx.AndroidStaticRule("rustc",
30		blueprint.RuleParams{
31			Command: "$envVars $rustcCmd " +
32				"-C linker=${config.RustLinker} " +
33				"-C link-args=\"${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}\" " +
34				"--emit link -o $out --emit dep-info=$out.d $in ${libFlags} $rustcFlags",
35			CommandDeps: []string{"$rustcCmd"},
36			// Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
37			Deps:    blueprint.DepsGCC,
38			Depfile: "$out.d",
39		},
40		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
41
42	_            = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
43	clippyDriver = pctx.AndroidStaticRule("clippy",
44		blueprint.RuleParams{
45			Command: "$envVars $clippyCmd " +
46				// Because clippy-driver uses rustc as backend, we need to have some output even during the linting.
47				// Use the metadata output as it has the smallest footprint.
48				"--emit metadata -o $out $in ${libFlags} " +
49				"$rustcFlags $clippyFlags",
50			CommandDeps: []string{"$clippyCmd"},
51		},
52		"rustcFlags", "libFlags", "clippyFlags", "envVars")
53
54	zip = pctx.AndroidStaticRule("zip",
55		blueprint.RuleParams{
56			Command:        "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
57			CommandDeps:    []string{"${SoongZipCmd}"},
58			Rspfile:        "$out.rsp",
59			RspfileContent: "$in",
60		})
61
62	cp = pctx.AndroidStaticRule("cp",
63		blueprint.RuleParams{
64			Command:        "cp `cat $outDir.rsp` $outDir",
65			Rspfile:        "${outDir}.rsp",
66			RspfileContent: "$in",
67		},
68		"outDir")
69)
70
71type buildOutput struct {
72	outputFile   android.Path
73	coverageFile android.Path
74}
75
76func init() {
77	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
78}
79
80func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
81	outputFile android.WritablePath, linkDirs []string) buildOutput {
82	flags.RustFlags = append(flags.RustFlags, "-C lto")
83
84	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin", linkDirs)
85}
86
87func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
88	outputFile android.WritablePath, linkDirs []string) buildOutput {
89	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib", linkDirs)
90}
91
92func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
93	outputFile android.WritablePath, linkDirs []string) buildOutput {
94	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib", linkDirs)
95}
96
97func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
98	outputFile android.WritablePath, linkDirs []string) buildOutput {
99	flags.RustFlags = append(flags.RustFlags, "-C lto")
100	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib", linkDirs)
101}
102
103func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
104	outputFile android.WritablePath, linkDirs []string) buildOutput {
105	flags.RustFlags = append(flags.RustFlags, "-C lto")
106	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib", linkDirs)
107}
108
109func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
110	flags Flags, outputFile android.WritablePath, linkDirs []string) buildOutput {
111	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro", linkDirs)
112}
113
114func rustLibsToPaths(libs RustLibraries) android.Paths {
115	var paths android.Paths
116	for _, lib := range libs {
117		paths = append(paths, lib.Path)
118	}
119	return paths
120}
121
122func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
123	outputFile android.WritablePath, crate_type string, linkDirs []string) buildOutput {
124
125	var inputs android.Paths
126	var implicits android.Paths
127	var envVars []string
128	var output buildOutput
129	var libFlags, rustcFlags, linkFlags []string
130	var implicitOutputs android.WritablePaths
131
132	output.outputFile = outputFile
133	crate_name := ctx.RustModule().CrateName()
134	targetTriple := ctx.toolchain().RustTriple()
135
136	inputs = append(inputs, main)
137
138	// Collect rustc flags
139	rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
140	rustcFlags = append(rustcFlags, flags.RustFlags...)
141	rustcFlags = append(rustcFlags, "--crate-type="+crate_type)
142	if crate_name != "" {
143		rustcFlags = append(rustcFlags, "--crate-name="+crate_name)
144	}
145	if targetTriple != "" {
146		rustcFlags = append(rustcFlags, "--target="+targetTriple)
147		linkFlags = append(linkFlags, "-target "+targetTriple)
148	}
149
150	// Suppress an implicit sysroot
151	rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
152
153	// Collect linker flags
154	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
155	linkFlags = append(linkFlags, flags.LinkFlags...)
156
157	// Collect library/crate flags
158	for _, lib := range deps.RLibs {
159		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
160	}
161	for _, lib := range deps.DyLibs {
162		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
163	}
164	for _, proc_macro := range deps.ProcMacros {
165		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
166	}
167
168	for _, path := range linkDirs {
169		libFlags = append(libFlags, "-L "+path)
170	}
171
172	// Collect dependencies
173	implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
174	implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
175	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
176	implicits = append(implicits, deps.StaticLibs...)
177	implicits = append(implicits, deps.SharedLibs...)
178
179	if deps.CrtBegin.Valid() {
180		implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
181	}
182
183	if flags.Coverage {
184		var gcnoFile android.WritablePath
185		// Provide consistency with cc gcda output, see cc/builder.go init()
186		profileEmitArg := strings.TrimPrefix(cc.PwdPrefix(), "PWD=") + "/"
187
188		if outputFile.Ext() != "" {
189			gcnoFile = android.PathForModuleOut(ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcno"))
190			rustcFlags = append(rustcFlags, "-Z profile-emit="+profileEmitArg+android.PathForModuleOut(
191				ctx, pathtools.ReplaceExtension(outputFile.Base(), "gcda")).String())
192		} else {
193			gcnoFile = android.PathForModuleOut(ctx, outputFile.Base()+".gcno")
194			rustcFlags = append(rustcFlags, "-Z profile-emit="+profileEmitArg+android.PathForModuleOut(
195				ctx, outputFile.Base()+".gcda").String())
196		}
197
198		implicitOutputs = append(implicitOutputs, gcnoFile)
199		output.coverageFile = gcnoFile
200	}
201
202	if len(deps.SrcDeps) > 0 {
203		genSubDir := "out/"
204		moduleGenDir := android.PathForModuleOut(ctx, genSubDir)
205		var outputs android.WritablePaths
206
207		for _, genSrc := range deps.SrcDeps {
208			if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) {
209				ctx.PropertyErrorf("srcs",
210					"multiple source providers generate the same filename output: "+genSrc.Base())
211			}
212			outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base()))
213		}
214
215		ctx.Build(pctx, android.BuildParams{
216			Rule:        cp,
217			Description: "cp " + moduleGenDir.Rel(),
218			Outputs:     outputs,
219			Inputs:      deps.SrcDeps,
220			Args: map[string]string{
221				"outDir": moduleGenDir.String(),
222			},
223		})
224		implicits = append(implicits, outputs.Paths()...)
225		envVars = append(envVars, "OUT_DIR=$$PWD/"+moduleGenDir.String())
226	}
227
228	if flags.Clippy {
229		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
230		ctx.Build(pctx, android.BuildParams{
231			Rule:            clippyDriver,
232			Description:     "clippy " + main.Rel(),
233			Output:          clippyFile,
234			ImplicitOutputs: nil,
235			Inputs:          inputs,
236			Implicits:       implicits,
237			Args: map[string]string{
238				"rustcFlags":  strings.Join(rustcFlags, " "),
239				"libFlags":    strings.Join(libFlags, " "),
240				"clippyFlags": strings.Join(flags.ClippyFlags, " "),
241				"envVars":     strings.Join(envVars, " "),
242			},
243		})
244		// Declare the clippy build as an implicit dependency of the original crate.
245		implicits = append(implicits, clippyFile)
246	}
247
248	ctx.Build(pctx, android.BuildParams{
249		Rule:            rustc,
250		Description:     "rustc " + main.Rel(),
251		Output:          outputFile,
252		ImplicitOutputs: implicitOutputs,
253		Inputs:          inputs,
254		Implicits:       implicits,
255		Args: map[string]string{
256			"rustcFlags": strings.Join(rustcFlags, " "),
257			"linkFlags":  strings.Join(linkFlags, " "),
258			"libFlags":   strings.Join(libFlags, " "),
259			"crtBegin":   deps.CrtBegin.String(),
260			"crtEnd":     deps.CrtEnd.String(),
261			"envVars":    strings.Join(envVars, " "),
262		},
263	})
264
265	return output
266}
267
268func TransformCoverageFilesToZip(ctx ModuleContext,
269	covFiles android.Paths, baseName string) android.OptionalPath {
270	if len(covFiles) > 0 {
271
272		outputFile := android.PathForModuleOut(ctx, baseName+".zip")
273
274		ctx.Build(pctx, android.BuildParams{
275			Rule:        zip,
276			Description: "zip " + outputFile.Base(),
277			Inputs:      covFiles,
278			Output:      outputFile,
279		})
280
281		return android.OptionalPathForPath(outputFile)
282	}
283	return android.OptionalPath{}
284}
285