Merge "Merge Android 12"
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 6648a2f..974f8f5 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -587,13 +587,11 @@
 %s
 
 def get_arch(target):
-  # TODO(b/199363072): filegroups and file targets aren't associated with any
-  # specific platform architecture in mixed builds. This is consistent with how
-  # Soong treats filegroups, but it may not be the case with manually-written
-  # filegroup BUILD targets.
-  if target.kind in ["filegroup", ""]:
-    return "common"
   buildoptions = build_options(target)
+  if buildoptions == None:
+    # File targets do not have buildoptions. File targets aren't associated with
+    #  any specific platform architecture in mixed builds.
+    return "common"
   platforms = build_options(target)["//command_line_option:platforms"]
   if len(platforms) != 1:
     # An individual configured target should have only one platform architecture.
diff --git a/android/filegroup.go b/android/filegroup.go
index 2cf5567..f8f0955 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -112,15 +112,22 @@
 	return module
 }
 
-func (fg *fileGroup) GenerateBazelBuildActions(ctx ModuleContext) bool {
+func (fg *fileGroup) maybeGenerateBazelBuildActions(ctx ModuleContext) {
 	if !fg.MixedBuildsEnabled(ctx) {
-		return false
+		return
+	}
+
+	archVariant := ctx.Arch().ArchType
+	if len(fg.Srcs()) == 1 && fg.Srcs()[0].Base() == fg.Name() {
+		// This will be a regular file target, not filegroup, in Bazel.
+		// See FilegroupBp2Build for more information.
+		archVariant = Common
 	}
 
 	bazelCtx := ctx.Config().BazelContext
-	filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), Common)
+	filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), archVariant)
 	if !ok {
-		return false
+		return
 	}
 
 	bazelOuts := make(Paths, 0, len(filePaths))
@@ -130,19 +137,15 @@
 	}
 
 	fg.srcs = bazelOuts
-
-	return true
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
-	if fg.GenerateBazelBuildActions(ctx) {
-		return
-	}
-
 	fg.srcs = PathsForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs)
 	if fg.properties.Path != nil {
 		fg.srcs = PathsWithModuleSrcSubDir(ctx, fg.srcs, String(fg.properties.Path))
 	}
+
+	fg.maybeGenerateBazelBuildActions(ctx)
 }
 
 func (fg *fileGroup) Srcs() Paths {
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 0d65822..bfe88f5 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -15,6 +15,7 @@
 package bp2build
 
 import (
+	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -1142,6 +1143,81 @@
 
 func TestCCLibraryNoCrtTrue(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library - nocrt: true emits attribute",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    nocrt: true,
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo-lib",
+    link_crt = False,
+    srcs = ["impl.cpp"],
+)`}})
+}
+
+func TestCCLibraryNoCrtFalse(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library - nocrt: false - does not emit attribute",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    nocrt: false,
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo-lib",
+    srcs = ["impl.cpp"],
+)`}})
+}
+
+func TestCCLibraryNoCrtArchVariant(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library - nocrt in select",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    arch: {
+        arm: {
+            nocrt: true,
+        },
+        x86: {
+            nocrt: false,
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		expectedErr: fmt.Errorf("Android.bp:16:1: module \"foo-lib\": nocrt is not supported for arch variants"),
+	})
+}
+
+func TestCCLibraryNoLibCrtTrue(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
 		description:                        "cc_library - simple example",
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
@@ -1165,7 +1241,7 @@
 )`}})
 }
 
-func TestCCLibraryNoCrtFalse(t *testing.T) {
+func TestCCLibraryNoLibCrtFalse(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
@@ -1189,7 +1265,7 @@
 )`}})
 }
 
-func TestCCLibraryNoCrtArchVariant(t *testing.T) {
+func TestCCLibraryNoLibCrtArchVariant(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
@@ -1223,41 +1299,6 @@
 )`}})
 }
 
-func TestCCLibraryNoCrtArchVariantWithDefault(t *testing.T) {
-	runCcLibraryTestCase(t, bp2buildTestCase{
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		filesystem: map[string]string{
-			"impl.cpp": "",
-		},
-		blueprint: soongCcLibraryPreamble + `
-cc_library {
-    name: "foo-lib",
-    srcs: ["impl.cpp"],
-    no_libcrt: false,
-    arch: {
-        arm: {
-            no_libcrt: true,
-        },
-        x86: {
-            no_libcrt: true,
-        },
-    },
-    include_build_directory: false,
-}
-`,
-		expectedBazelTargets: []string{`cc_library(
-    name = "foo-lib",
-    srcs = ["impl.cpp"],
-    use_libcrt = select({
-        "//build/bazel/platforms/arch:arm": False,
-        "//build/bazel/platforms/arch:x86": False,
-        "//conditions:default": True,
-    }),
-)`}})
-}
-
 func TestCcLibraryStrip(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
 		description:                        "cc_library strip args",
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 3dcfbd7..b272067 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -15,6 +15,7 @@
 package bp2build
 
 import (
+	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -364,3 +365,78 @@
 )`},
 	})
 }
+
+func TestCcLibrarySharedNoCrtTrue(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared - nocrt: true emits attribute",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    srcs: ["impl.cpp"],
+    nocrt: true,
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    link_crt = False,
+    srcs = ["impl.cpp"],
+)`}})
+}
+
+func TestCcLibrarySharedNoCrtFalse(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared - nocrt: false doesn't emit attribute",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    srcs: ["impl.cpp"],
+    nocrt: false,
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: []string{`cc_library_shared(
+    name = "foo_shared",
+    srcs = ["impl.cpp"],
+)`}})
+}
+
+func TestCcLibrarySharedNoCrtArchVariant(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_shared - nocrt in select",
+		moduleTypeUnderTest:                "cc_library_shared",
+		moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library_shared {
+    name: "foo_shared",
+    srcs: ["impl.cpp"],
+    arch: {
+        arm: {
+            nocrt: true,
+        },
+        x86: {
+            nocrt: false,
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		expectedErr: fmt.Errorf("Android.bp:16:1: module \"foo_shared\": nocrt is not supported for arch variants"),
+	})
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 22bd90b..ebba9d1 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -364,6 +364,7 @@
 	wholeArchiveDeps          bazel.LabelListAttribute
 	systemDynamicDeps         bazel.LabelListAttribute
 
+	linkCrt                       bazel.BoolAttribute
 	useLibcrt                     bazel.BoolAttribute
 	linkopts                      bazel.StringListAttribute
 	versionScript                 bazel.LabelAttribute
@@ -398,6 +399,7 @@
 
 	var linkopts bazel.StringListAttribute
 	var versionScript bazel.LabelAttribute
+	var linkCrt bazel.BoolAttribute
 	var useLibcrt bazel.BoolAttribute
 
 	var stripKeepSymbols bazel.BoolAttribute
@@ -418,6 +420,9 @@
 		}
 	}
 
+	// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
+	var disallowedArchVariantCrt bool
+
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
 		for config, props := range configToProps {
 			if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
@@ -457,10 +462,23 @@
 					versionScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
 				}
 				useLibcrt.SetSelectValue(axis, config, baseLinkerProps.libCrt())
+
+				// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
+				if baseLinkerProps.crt() != nil {
+					if axis == bazel.NoConfigAxis {
+						linkCrt.SetSelectValue(axis, config, baseLinkerProps.crt())
+					} else if axis == bazel.ArchConfigurationAxis {
+						disallowedArchVariantCrt = true
+					}
+				}
 			}
 		}
 	}
 
+	if disallowedArchVariantCrt {
+		ctx.ModuleErrorf("nocrt is not supported for arch variants")
+	}
+
 	type productVarDep struct {
 		// the name of the corresponding excludes field, if one exists
 		excludesField string
@@ -530,6 +548,7 @@
 		wholeArchiveDeps:          wholeArchiveDeps,
 		systemDynamicDeps:         systemSharedDeps,
 
+		linkCrt:       linkCrt,
 		linkopts:      linkopts,
 		useLibcrt:     useLibcrt,
 		versionScript: versionScript,
diff --git a/cc/library.go b/cc/library.go
index 77eddbf..58e0e21 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -252,6 +252,7 @@
 
 	// This is shared only.
 	Version_script bazel.LabelAttribute
+	Link_crt       bazel.BoolAttribute
 
 	// Common properties shared between both shared and static variants.
 	Shared staticOrSharedAttributes
@@ -321,6 +322,7 @@
 		Local_includes:              compilerAttrs.localIncludes,
 		Absolute_includes:           compilerAttrs.absoluteIncludes,
 		Linkopts:                    linkerAttrs.linkopts,
+		Link_crt:                    linkerAttrs.linkCrt,
 		Use_libcrt:                  linkerAttrs.useLibcrt,
 		Rtti:                        compilerAttrs.rtti,
 		Stl:                         compilerAttrs.stl,
@@ -2415,6 +2417,7 @@
 			Asflags:    asFlags,
 			Linkopts:   linkerAttrs.linkopts,
 
+			Link_crt:   linkerAttrs.linkCrt,
 			Use_libcrt: linkerAttrs.useLibcrt,
 			Rtti:       compilerAttrs.rtti,
 			Stl:        compilerAttrs.stl,
@@ -2472,6 +2475,7 @@
 	staticOrSharedAttributes
 
 	Linkopts   bazel.StringListAttribute
+	Link_crt   bazel.BoolAttribute // Only for linking shared library (and cc_binary)
 	Use_libcrt bazel.BoolAttribute
 	Rtti       bazel.BoolAttribute
 	Stl        *string
diff --git a/cc/linker.go b/cc/linker.go
index 0d612b5..20e377c 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -238,6 +238,19 @@
 	return &ret
 }
 
+func (blp *BaseLinkerProperties) crt() *bool {
+	val := invertBoolPtr(blp.Nocrt)
+	if val != nil && *val {
+		// == True
+		//
+		// Since crt is enabled for almost every module compiling against the Bionic runtime,
+		// use `nil` when it's enabled, and rely on the Starlark macro to set it to True by default.
+		// This keeps the BUILD files clean.
+		return nil
+	}
+	return val // can be False or nil
+}
+
 func (blp *BaseLinkerProperties) libCrt() *bool {
 	return invertBoolPtr(blp.No_libcrt)
 }
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 1bdd040..658e8e2 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -598,11 +598,18 @@
 func toJsonClassLoaderContextRec(clcs []*ClassLoaderContext) []*jsonClassLoaderContext {
 	jClcs := make([]*jsonClassLoaderContext, len(clcs))
 	for i, clc := range clcs {
+		var host string
+		if clc.Host == nil {
+			// Defer build failure to when this CLC is actually used.
+			host = fmt.Sprintf("implementation-jar-for-%s-is-not-available.jar", clc.Name)
+		} else {
+			host = clc.Host.String()
+		}
 		jClcs[i] = &jsonClassLoaderContext{
 			Name:        clc.Name,
 			Optional:    clc.Optional,
 			Implicit:    clc.Implicit,
-			Host:        clc.Host.String(),
+			Host:        host,
 			Device:      clc.Device,
 			Subcontexts: toJsonClassLoaderContextRec(clc.Subcontexts),
 		}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 7fd88fc..0c66ccf 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -25,6 +25,9 @@
 	"android/soong/remoteexec"
 )
 
+// The values allowed for Droidstubs' Api_levels_sdk_type
+var allowedApiLevelSdkTypes = []string{"public", "system", "module-lib"}
+
 func init() {
 	RegisterStubsBuildComponents(android.InitRegistrationContext)
 }
@@ -134,7 +137,7 @@
 	// the dirs which Metalava extracts API levels annotations from.
 	Api_levels_annotations_dirs []string
 
-	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public' and 'system' for now; defaults to public.
+	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public', 'system' and 'module-lib' for now; defaults to public.
 	Api_levels_sdk_type *string
 
 	// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
@@ -404,19 +407,24 @@
 	// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
 	// an actual file present on disk (in the order the patterns were passed). For system APIs for
 	// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
-	// for older releases.
-	if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" {
-		if sdkType != "system" {
-			ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported")
-		}
-		// If building non public stubs, add all sdkType patterns first...
-		for _, dir := range dirs {
-			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename))
-		}
+	// for older releases. Similarly, module-lib falls back to system API.
+	var sdkDirs []string
+	switch proptools.StringDefault(d.properties.Api_levels_sdk_type, "public") {
+	case "module-lib":
+		sdkDirs = []string{"module-lib", "system", "public"}
+	case "system":
+		sdkDirs = []string{"system", "public"}
+	case "public":
+		sdkDirs = []string{"public"}
+	default:
+		ctx.PropertyErrorf("api_levels_sdk_type", "needs to be one of %v", allowedApiLevelSdkTypes)
+		return
 	}
-	for _, dir := range dirs {
-		// ... and fallback to public ones, for Metalava to use if needed.
-		cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename))
+
+	for _, sdkDir := range sdkDirs {
+		for _, dir := range dirs {
+			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename))
+		}
 	}
 }
 
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 60d0bea..10d99f3 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"fmt"
 	"reflect"
 	"regexp"
 	"strings"
@@ -82,8 +83,10 @@
 	}
 }
 
-func TestSystemDroidstubs(t *testing.T) {
-	ctx, _ := testJavaWithFS(t, `
+// runs a test for droidstubs with a customizable sdkType argument and returns
+// the list of jar patterns that is passed as `--android-jar-pattern`
+func getAndroidJarPatternsForDroidstubs(t *testing.T, sdkType string) []string {
+	ctx, _ := testJavaWithFS(t, fmt.Sprintf(`
 		droiddoc_exported_dir {
 			name: "some-exported-dir",
 			path: "somedir",
@@ -102,9 +105,9 @@
 				"some-other-exported-dir",
 			],
 			api_levels_annotations_enabled: true,
-            api_levels_sdk_type: "system",
+      api_levels_sdk_type: "%s",
 		}
-		`,
+		`, sdkType),
 		map[string][]byte{
 			"foo-doc/a.java": nil,
 		})
@@ -113,13 +116,40 @@
 	manifest := m.Output("metalava.sbox.textproto")
 	cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
 	r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`)
-	matches := r.FindAllString(cmd, -1)
+	return r.FindAllString(cmd, -1)
+}
+
+func TestPublicDroidstubs(t *testing.T) {
+	patterns := getAndroidJarPatternsForDroidstubs(t, "public")
+
+	android.AssertArrayString(t, "order of patterns", []string{
+		"--android-jar-pattern somedir/%/public/android.jar",
+		"--android-jar-pattern someotherdir/%/public/android.jar",
+	}, patterns)
+}
+
+func TestSystemDroidstubs(t *testing.T) {
+	patterns := getAndroidJarPatternsForDroidstubs(t, "system")
+
 	android.AssertArrayString(t, "order of patterns", []string{
 		"--android-jar-pattern somedir/%/system/android.jar",
 		"--android-jar-pattern someotherdir/%/system/android.jar",
 		"--android-jar-pattern somedir/%/public/android.jar",
 		"--android-jar-pattern someotherdir/%/public/android.jar",
-	}, matches)
+	}, patterns)
+}
+
+func TestModuleLibDroidstubs(t *testing.T) {
+	patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib")
+
+	android.AssertArrayString(t, "order of patterns", []string{
+		"--android-jar-pattern somedir/%/module-lib/android.jar",
+		"--android-jar-pattern someotherdir/%/module-lib/android.jar",
+		"--android-jar-pattern somedir/%/system/android.jar",
+		"--android-jar-pattern someotherdir/%/system/android.jar",
+		"--android-jar-pattern somedir/%/public/android.jar",
+		"--android-jar-pattern someotherdir/%/public/android.jar",
+	}, patterns)
 }
 
 func TestDroidstubsSandbox(t *testing.T) {
diff --git a/rust/project_json.go b/rust/project_json.go
index faa7db5..ae48312 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -51,6 +51,7 @@
 	Deps        []rustProjectDep  `json:"deps"`
 	Cfg         []string          `json:"cfg"`
 	Env         map[string]string `json:"env"`
+	ProcMacro   bool              `json:"is_proc_macro"`
 }
 
 type rustProjectJson struct {
@@ -208,6 +209,8 @@
 		comp = c.baseCompiler
 	case *testDecorator:
 		comp = c.binaryDecorator.baseCompiler
+	case *procMacroDecorator:
+		comp = c.baseCompiler
 	default:
 		return nil, nil, false
 	}
@@ -224,6 +227,8 @@
 		return 0, false
 	}
 
+	_, procMacro := rModule.compiler.(*procMacroDecorator)
+
 	crate := rustProjectCrate{
 		DisplayName: rModule.Name(),
 		RootModule:  rootModule,
@@ -231,6 +236,7 @@
 		Deps:        make([]rustProjectDep, 0),
 		Cfg:         make([]string, 0),
 		Env:         make(map[string]string),
+		ProcMacro:   procMacro,
 	}
 
 	if comp.CargoOutDir().Valid() {
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index f7b6681..255b2e5 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -117,6 +117,58 @@
 	validateJsonCrates(t, jsonContent)
 }
 
+func TestProjectJsonProcMacroDep(t *testing.T) {
+	bp := `
+	rust_proc_macro {
+		name: "libproc_macro",
+		srcs: ["a/src/lib.rs"],
+		crate_name: "proc_macro"
+	}
+	rust_library {
+		name: "librust",
+		srcs: ["b/src/lib.rs"],
+		crate_name: "rust",
+		proc_macros: ["libproc_macro"],
+	}
+	`
+	jsonContent := testProjectJson(t, bp)
+	crates := validateJsonCrates(t, jsonContent)
+	libproc_macro_count := 0
+	librust_count := 0
+	for _, c := range crates {
+		crate := validateCrate(t, c)
+		procMacro, ok := crate["is_proc_macro"].(bool)
+		if !ok {
+			t.Fatalf("Unexpected type for is_proc_macro: %v", crate["is_proc_macro"])
+		}
+
+		name, ok := crate["display_name"].(string)
+		if !ok {
+			t.Fatalf("Unexpected type for display_name: %v", crate["display_name"])
+		}
+
+		switch name {
+		case "libproc_macro":
+			libproc_macro_count += 1
+			if !procMacro {
+				t.Fatalf("'libproc_macro' is marked with is_proc_macro=false")
+			}
+		case "librust":
+			librust_count += 1
+			if procMacro {
+				t.Fatalf("'librust' is not a proc macro crate, but is marked with is_proc_macro=true")
+			}
+		default:
+			break
+		}
+	}
+
+	if libproc_macro_count != 1 || librust_count != 1 {
+		t.Fatalf("Unexpected crate counts: libproc_macro_count: %v, librust_count: %v",
+			libproc_macro_count, librust_count)
+	}
+}
+
 func TestProjectJsonFeature(t *testing.T) {
 	bp := `
 	rust_library {