Skip to content

Commit 6b9bc32

Browse files
authored
Add declared crate features to the Rust sync tool (#573)
## Usage and product changes Introduce `declared_features` to the Rust sync tool, parsed from the tags of Bazel `rust_` targets, to generate `Cargo.toml` files with predeclared features that are not automatically used in the dependent targets (this happens with `crate_features`). ## Motivation We need a way to declare features without their automatic usage to be able to activate them manually through `cargo` only when needed. ## Implementation Parse `tags` in order to search for a new tag containing a list of declared features. Save these features into a new separate field which is then passed to the crate features declaration.
1 parent cf9c170 commit 6b9bc32

File tree

2 files changed

+30
-14
lines changed

2 files changed

+30
-14
lines changed

builder/rust/cargo/project_aspect.bzl

+19-12
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@ _BIN_ENTRY_POINT = "main.rs"
1919
_LIB_ENTRY_POINT = "lib.rs"
2020

2121
CrateInfo = provider(fields = [
22-
"kind", # str
23-
"crate_name", # str
24-
"workspace_name", # str
25-
"crate_path", # str
26-
"version", # str
27-
"features", # List[str]
28-
"deps", # List[target]
29-
"transitive_deps", # List[target]
30-
"build_deps", # List[target]
22+
"kind", # str
23+
"crate_name", # str
24+
"workspace_name", # str
25+
"crate_path", # str
26+
"version", # str
27+
"enabled_features", # List[str]
28+
"features", # List[str]
29+
"deps", # List[target]
30+
"transitive_deps", # List[target]
31+
"build_deps", # List[target]
3132
])
3233

3334
CargoProjectInfo = provider(fields = [
@@ -102,13 +103,17 @@ rust_cargo_properties_aspect = aspect(
102103
)
103104

104105
def _crate_info(ctx, target):
106+
features = []
105107
if _is_universe_crate(target):
106108
crate_name = str(target.label).split(".")[0].rsplit("-", 1)[0].removeprefix("@crates__")
107109
else:
108110
crate_name = ctx.rule.attr.name
109111
for tag in ctx.rule.attr.tags:
110112
if tag.startswith("crate-name"):
111113
crate_name = tag.split("=")[1]
114+
elif tag.startswith("declared-features"):
115+
feature_str = tag.split("=")[1]
116+
features = [f.strip() for f in feature_str.split(",") if f.strip()]
112117

113118
workspace_name = target.label.workspace_name
114119
crate_path = target.label.package
@@ -121,7 +126,8 @@ def _crate_info(ctx, target):
121126
workspace_name = workspace_name,
122127
crate_path = crate_path,
123128
version = getattr(ctx.rule.attr, "version", "0.0.0"),
124-
features = getattr(ctx.rule.attr, "crate_features", []),
129+
enabled_features = getattr(ctx.rule.attr, "crate_features", []),
130+
features = features,
125131
deps = deps,
126132
transitive_deps = transitive_deps,
127133
build_deps = _crate_build_deps(ctx, target),
@@ -227,6 +233,7 @@ def _get_properties(target, ctx, source_files, crate_info):
227233
properties = {}
228234
properties["name"] = crate_info.crate_name
229235
properties["path"] = crate_info.crate_path
236+
properties["enabled.features"] = ",".join(crate_info.enabled_features)
230237
properties["features"] = ",".join(crate_info.features)
231238
properties["target.name"] = target.label.name
232239
properties["type"] = target_type
@@ -259,8 +266,8 @@ def _crate_deps_info(target, crate_info):
259266
repository_relative_path = target_to_root + "/" + root_to_dep
260267
location = "path=../{};localpath={}".format(dependency_info.crate_name, repository_relative_path)
261268

262-
features = ",".join(dependency_info.features)
263-
info = location + (";features={}".format(features) if features else "")
269+
enabled_features = ",".join(dependency_info.enabled_features)
270+
info = location + (";enabled.features={}".format(enabled_features) if enabled_features else "")
264271
deps_info[dependency_info.crate_name] = info
265272
return deps_info
266273

tool/ide/RustManifestSyncer.kt

+11-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.Paths
3434
import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.TargetProperties.Keys.BUILD_DEPS
3535
import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.TargetProperties.Keys.DEPS_PREFIX
3636
import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.TargetProperties.Keys.EDITION
37+
import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.TargetProperties.Keys.ENABLED_FEATURES
3738
import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.TargetProperties.Keys.ENTRY_POINT_PATH
3839
import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.TargetProperties.Keys.FEATURES
3940
import com.typedb.dependencies.tool.ide.RustManifestSyncer.WorkspaceSyncer.TargetProperties.Keys.LOCAL_PATH
@@ -263,7 +264,9 @@ class RustManifestSyncer : Callable<Unit> {
263264

264265
cargoToml.createSubConfig().apply {
265266
cargoToml.set<Config>("features", this)
266-
properties.features.forEach { set<Config>(it, emptyList<String>()) }
267+
(properties.enabledFeatures + properties.features)
268+
.distinct()
269+
.forEach { set<Config>(it, emptyList<String>()) }
267270
}
268271

269272
cargoToml.createEntryPointSubConfig()
@@ -404,6 +407,7 @@ class RustManifestSyncer : Callable<Unit> {
404407
val targetName: String,
405408
val cratePath: String,
406409
val type: Type,
410+
val enabledFeatures: Collection<String>,
407411
val features: Collection<String>,
408412
val version: String,
409413
val edition: String?,
@@ -483,7 +487,7 @@ class RustManifestSyncer : Callable<Unit> {
483487
val name = rawKey.split(".", limit = 2)[1]
484488
val rawValueProps = rawValue.split(";")
485489
.associate { it.split("=", limit = 2).let { parts -> parts[0] to parts[1] } }
486-
val features = rawValueProps[FEATURES]?.split(",")?.filter { it != "bazel" } ?: emptyList();
490+
val features = rawValueProps[ENABLED_FEATURES]?.split(",")?.filter { it != "bazel" } ?: emptyList();
487491
return if (VERSION in rawValueProps) {
488492
Crate(
489493
name = name,
@@ -542,6 +546,7 @@ class RustManifestSyncer : Callable<Unit> {
542546
name = props.getProperty(NAME),
543547
targetName = props.getProperty(TARGET_NAME),
544548
type = Type.of(props.getProperty(TYPE)),
549+
enabledFeatures = props.getProperty(ENABLED_FEATURES).split(",").filter { it.isNotBlank() },
545550
features = props.getProperty(FEATURES).split(",").filter { it.isNotBlank() },
546551
version = props.getProperty(VERSION),
547552
edition = props.getProperty(EDITION, "2021"),
@@ -567,6 +572,7 @@ class RustManifestSyncer : Callable<Unit> {
567572
name = base.name,
568573
targetName = base.targetName,
569574
type = base.type,
575+
enabledFeatures = (base.enabledFeatures + properties.enabledFeatures).distinct(),
570576
features = (base.features + properties.features).distinct(),
571577
version = base.version,
572578
edition = base.edition,
@@ -595,6 +601,7 @@ class RustManifestSyncer : Callable<Unit> {
595601
name = first.cratePath.replace('/', '-'),
596602
targetName = first.targetName,
597603
type = first.type,
604+
enabledFeatures = first.enabledFeatures,
598605
features = first.features,
599606
version = first.version,
600607
edition = first.edition,
@@ -617,6 +624,7 @@ class RustManifestSyncer : Callable<Unit> {
617624
name = lib.name,
618625
targetName = lib.targetName,
619626
type = lib.type,
627+
enabledFeatures = lib.enabledFeatures,
620628
features = lib.features,
621629
version = lib.version,
622630
edition = lib.edition,
@@ -649,6 +657,7 @@ class RustManifestSyncer : Callable<Unit> {
649657
const val DEPS_PREFIX = "deps"
650658
const val EDITION = "edition"
651659
const val ENTRY_POINT_PATH = "entry.point.path"
660+
const val ENABLED_FEATURES = "enabled.features"
652661
const val FEATURES = "features"
653662
const val TARGET_NAME = "target.name"
654663
const val NAME = "name"

0 commit comments

Comments
 (0)