Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
- Add flag for `rtl` target to files without target in script and sources.
- Add `pass_targets` to dependencies to allow passing targets for hierarchical file filtering (not only global).
- Add application of targets to only specific packages with a `<PKG>:<TARGET>` model for the target flag.
- Add target filtering for dependencies.

## 0.29.1 - 2025-11-24
### Fixed
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ dependencies:
common_verification: { git: "git@github.com:pulp-platform/common_verification.git", version: "0.1" }

# Git revision dependency.
common_cells: { git: "git@github.com:pulp-platform/common_cells.git", rev: master, pass_targets: ["CC_CUSTOM_TARGET"] }
common_cells: { target: test, git: "git@github.com:pulp-platform/common_cells.git", rev: master, pass_targets: ["CC_CUSTOM_TARGET", {target: "any(*, test)", pass: "cc_test"}] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would separate the target examples here since it is a bit overloaded. Then, for each type of target you can explain in the comment above what it does. For instance:

# Using a dependency only if (global?) target `test` is used
common_verification: {git: "git@github.com:pulp-platform/common_verification.git", version: 0.1.0, target: "test"}

# Passing a target to a dependency (equivalent to `-t cva6:cv64a6_imafdcv_sv39`)
cva6: {git: "git@github.com:pulp-platform/cva6.git", version: 0.1.0, pass_targets: "cv64a6_imafdcv_sv39"}

# Remapping a local target if a global target is passed
tech_cells_generic:
  - git: git@github.com:pulp-platform/tech_cells_generic.git
  - version: 0.1.0
  - pass_targets:
      - target: all(asic, tech7)
      - pass: tech7_macro # Maybe you have a better example

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also why is CC_CUSTOM_TARGET uppercase? It is kind of confusing since it gives the impression that this target should be upper case, but targets are case insensitive. Lowercase targets look better, and I would not want people to start using uppercase targets (even if it does not matter).


# Freeze any dependency updates. Optional. False if omitted.
# Useful for chip packages. Once the chip is in final tapeout mode, and
Expand Down Expand Up @@ -239,6 +239,14 @@ All git tags of the form `vX.Y.Z` are considered a version of the package.

[Relevant dependency resolution code](https://github.com/pulp-platform/bender/blob/master/src/resolver.rs)

#### Target handling

Specified dependencies can be filtered, similar to the sources below. For consistency, this filtering does **NOT** apply during an update, i.e., all dependencies will be accounted for in the Bender.lock file. The target filtering only applies for sources and script outputs. This can be used e.g., to include specific IP only for testing.

#### Passing targets

For sources and script generation, targets can be passed from a package to its dependency directly in the `Bender.yml` file. This allows for enabling and disabling of specific features. Furthermore, these passed targets can be again filtered with a target specification applied to the specific target. This can be used e.g., to enable specific features of dependencies.


### Sources

Expand Down
4 changes: 2 additions & 2 deletions src/cmd/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn run(sess: &Session, path: &Path, args: &CloneArgs) -> Result<()> {
// Check current config for matches
if sess.config.overrides.contains_key(dep) {
match &sess.config.overrides[dep] {
config::Dependency::Path(p, _) => {
config::Dependency::Path(_, p, _) => {
Err(Error::new(format!(
"Dependency `{}` already has a path override at\n\t{}\n\tPlease check Bender.local or .bender.yml",
dep,
Expand Down Expand Up @@ -344,7 +344,7 @@ pub fn get_path_subdeps(
}
.iter()
.filter_map(|(k, v)| match v {
config::Dependency::Path(p, _) => {
config::Dependency::Path(_, p, _) => {
if p.starts_with(&old_path) {
Some((
k.clone(),
Expand Down
2 changes: 0 additions & 2 deletions src/cmd/fusesoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ pub fn run_single(sess: &Session, args: &FusesocArgs) -> Result<()> {
sess.manifest.dependencies.keys().cloned().collect(),
IndexMap::new(),
version_string.clone(),
IndexMap::new(),
)
.flatten()),
None => Err(Error::new("Error in loading sources")),
Expand Down Expand Up @@ -522,7 +521,6 @@ fn get_fuse_depend_string(
files: group.files.clone(),
dependencies: group.dependencies.clone(),
version: version_string.clone(),
passed_targets: TargetSet::empty(),
})
.collect()
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/packages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ pub fn run(sess: &Session, args: &PackagesArgs) -> Result<()> {
let mut version_str = String::from("");
for pkgs in sess.packages().iter() {
let pkg_names = pkgs.iter().map(|&id| sess.dependency_name(id));
let pkg_sources = pkgs.iter().map(|&id| sess.dependency(id));
if args.version {
let pkg_sources = pkgs.iter().map(|&id| sess.dependency(id));
for pkg_source in pkg_sources {
version_str.push_str(&format!(
"{}:\t{}\tat {}\t{}\n",
Expand Down
29 changes: 20 additions & 9 deletions src/cmd/parents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ pub fn run(sess: &Session, args: &ParentsArgs) -> Result<()> {
if args.targets {
let mut res = String::from("");
for (k, v) in parent_array.iter() {
res.push_str(&format!(" {}\tpasses: {:?}\n", k, v).to_string());
res.push_str(
&format!(" {}\tfilters: {}\tpasses: {:?}\n", k, &v[0], &v[1..]).to_string(),
);
}
let mut tw = TabWriter::new(vec![]);
write!(&mut tw, "{}", res).unwrap();
Expand Down Expand Up @@ -121,10 +123,14 @@ pub fn get_parent_array(
map.insert(
sess.manifest.package.name.clone(),
match sess.manifest.dependencies.get(dep).unwrap() {
Dependency::Version(_, tgts) => tgts.clone(),
Dependency::Path(_, tgts) => tgts.clone(),
Dependency::GitRevision(_, _, tgts) => tgts.clone(),
Dependency::GitVersion(_, _, tgts) => tgts.clone(),
Dependency::Version(targetspec, _, tgts)
| Dependency::Path(targetspec, _, tgts)
| Dependency::GitRevision(targetspec, _, _, tgts)
| Dependency::GitVersion(targetspec, _, _, tgts) => {
let mut tgts = tgts.iter().map(|t| t.to_string()).collect::<Vec<_>>();
tgts.insert(0, targetspec.to_string());
tgts
}
},
);
} else {
Expand Down Expand Up @@ -161,10 +167,15 @@ pub fn get_parent_array(
map.insert(
pkg_name.to_string(),
match dep_manifest.dependencies.get(dep).unwrap() {
Dependency::Version(_, tgts) => tgts.clone(),
Dependency::Path(_, tgts) => tgts.clone(),
Dependency::GitRevision(_, _, tgts) => tgts.clone(),
Dependency::GitVersion(_, _, tgts) => tgts.clone(),
Dependency::Version(targetspec, _, tgts)
| Dependency::Path(targetspec, _, tgts)
| Dependency::GitRevision(targetspec, _, _, tgts)
| Dependency::GitVersion(targetspec, _, _, tgts) => {
let mut tgts =
tgts.iter().map(|t| t.to_string()).collect::<Vec<_>>();
tgts.insert(0, targetspec.to_string());
tgts
}
},
);
} else {
Expand Down
34 changes: 26 additions & 8 deletions src/cmd/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use serde::Serialize;
use tera::{Context, Tera};
use tokio::runtime::Runtime;

use crate::cmd::sources::get_passed_targets;
use crate::config::Validate;
use crate::error::*;
use crate::sess::{Session, SessionIo};
Expand Down Expand Up @@ -263,21 +264,38 @@ pub fn run(sess: &Session, args: &ScriptArgs) -> Result<()> {
srcs = srcs.assign_target("rtl".to_string());
}

srcs = srcs
.filter_targets(&targets, !args.ignore_passed_targets)
.unwrap_or_default();

// Filter the sources by specified packages.
let packages = &srcs.get_package_list(
sess,
sess.manifest.package.name.to_string(),
&get_package_strings(&args.package),
&get_package_strings(&args.exclude),
args.no_deps,
);

if !args.package.is_empty() || !args.exclude.is_empty() || args.no_deps {
srcs = srcs.filter_packages(packages).unwrap_or_default();
}
let (all_targets, used_packages) = get_passed_targets(
sess,
&rt,
&io,
&targets,
packages,
&get_package_strings(&args.package),
)?;

let targets = if args.ignore_passed_targets {
targets
} else {
all_targets
};

let packages = if args.ignore_passed_targets {
packages.clone()
} else {
used_packages
};

srcs = srcs.filter_targets(&targets).unwrap_or_default();

srcs = srcs.filter_packages(&packages).unwrap_or_default();

// Flatten and validate the sources.
let srcs = srcs
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn run(sess: &Session, args: &SnapshotArgs) -> Result<()> {

// Loop through existing deps to find the ones that are overridden to the working directory
for (name, dep) in sess.config.overrides.iter() {
if let Dependency::Path(override_path, _) = dep {
if let Dependency::Path(_, override_path, _) = dep {
if override_path.starts_with(sess.root.join(&args.working_dir)) {
if let DependencySource::Path(dep_path) =
sess.dependency_source(sess.dependency_with_name(name)?)
Expand Down
120 changes: 110 additions & 10 deletions src/cmd/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
//! The `sources` subcommand.

use std;
use std::collections::BTreeSet;
use std::io::Write;

use clap::{ArgAction, Args};
use futures::future::join_all;
use indexmap::IndexSet;
use serde_json;
use tokio::runtime::Runtime;

use crate::config::Validate;
use crate::config::{Dependency, Validate};
use crate::error::*;
use crate::sess::{Session, SessionIo};
use crate::target::TargetSet;
use crate::target::{TargetSet, TargetSpec};

/// Emit the source file manifest for the package
#[derive(Args, Debug)]
Expand Down Expand Up @@ -83,21 +85,38 @@ pub fn run(sess: &Session, args: &SourcesArgs) -> Result<()> {
srcs = srcs.assign_target("rtl".to_string());
}

srcs = srcs
.filter_targets(&targets, args.ignore_passed_targets)
.unwrap_or_default();

// Filter the sources by specified packages.
let packages = &srcs.get_package_list(
sess,
sess.manifest.package.name.to_string(),
&get_package_strings(&args.package),
&get_package_strings(&args.exclude),
args.no_deps,
);

if !args.package.is_empty() || !args.exclude.is_empty() || args.no_deps {
srcs = srcs.filter_packages(packages).unwrap_or_default();
}
let (all_targets, used_packages) = get_passed_targets(
sess,
&rt,
&io,
&targets,
packages,
&get_package_strings(&args.package),
)?;

let targets = if args.ignore_passed_targets {
targets
} else {
all_targets
};

let packages = if args.ignore_passed_targets {
packages.clone()
} else {
used_packages
};

srcs = srcs.filter_targets(&targets).unwrap_or_default();

srcs = srcs.filter_packages(&packages).unwrap_or_default();

srcs = srcs.validate("", false, &sess.suppress_warnings)?;

Expand All @@ -114,3 +133,84 @@ pub fn run(sess: &Session, args: &SourcesArgs) -> Result<()> {
let _ = writeln!(std::io::stdout(),);
result.map_err(|cause| Error::chain("Failed to serialize source file manifest.", cause))
}

/// Get the targets passed to dependencies from calling packages.
pub fn get_passed_targets(
sess: &Session,
rt: &Runtime,
io: &SessionIo,
global_targets: &TargetSet,
used_packages: &IndexSet<String>,
required_packages: &IndexSet<String>,
) -> Result<(TargetSet, IndexSet<String>)> {
let mut global_targets = global_targets.clone();
let mut required_packages = required_packages.clone();
if used_packages.contains(&sess.manifest.package.name) {
required_packages.insert(sess.manifest.package.name.clone());
sess.manifest
.dependencies
.iter()
.for_each(|(name, dep)| match dep {
Dependency::Version(filter, _, tgts)
| Dependency::Path(filter, _, tgts)
| Dependency::GitRevision(filter, _, _, tgts)
| Dependency::GitVersion(filter, _, _, tgts) => {
for t in tgts {
if TargetSpec::All(BTreeSet::from([filter.clone(), t.target.clone()]))
.matches(&global_targets.reduce_for_dependency(name))
{
global_targets.insert(format!("{}:{}", name, t.pass));
}
}
if filter.matches(&global_targets.reduce_for_dependency(name)) {
required_packages.insert(name.clone());
}
}
})
};
for pkgs in sess.packages().iter().rev() {
let manifests = rt
.block_on(join_all(
pkgs.iter()
.map(|pkg| io.dependency_manifest(*pkg, false, &[])),
))
.into_iter()
.collect::<Result<Vec<_>>>()?;
manifests.into_iter().flatten().for_each(|manifest| {
let pkg_name = &manifest.package.name;
if used_packages.contains(pkg_name) && required_packages.contains(pkg_name) {
manifest
.dependencies
.iter()
.for_each(|(name, dep)| match dep {
Dependency::Version(filter, _, tgts)
| Dependency::Path(filter, _, tgts)
| Dependency::GitRevision(filter, _, _, tgts)
| Dependency::GitVersion(filter, _, _, tgts) => {
for t in tgts {
if TargetSpec::All(BTreeSet::from([
filter.clone(),
t.target.clone(),
]))
.matches(&global_targets.reduce_for_dependency(pkg_name))
{
global_targets.insert(format!("{}:{}", name, t.pass));
}
}
if filter.matches(&global_targets.reduce_for_dependency(pkg_name)) {
required_packages.insert(name.clone());
}
}
})
};
});
}

Ok((
global_targets,
required_packages
.intersection(used_packages)
.cloned()
.collect(),
))
}
2 changes: 1 addition & 1 deletion src/cmd/vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub fn run(sess: &Session, args: &VendorArgs) -> Result<()> {
)
}).await?;
let rev_hash = match vendor_package.upstream {
config::Dependency::GitRevision(_, ref rev, _) => Ok(rev),
config::Dependency::GitRevision(_, _, ref rev, _) => Ok(rev),
_ => Err(Error::new("Please ensure your vendor reference is a commit hash to avoid upstream changes impacting your checkout")),
}?;
git.clone().spawn_with(|c| c.arg("checkout").arg(rev_hash)).await?;
Expand Down
Loading