Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: workspace level patch table added #4086

Merged
merged 12 commits into from
Apr 19, 2023
20 changes: 20 additions & 0 deletions docs/book/src/forc/workspaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The key points for workspaces are:
Workspace manifests are declared within `Forc.toml` files and support the following fields:

* [`members`](#the-members-field) - Packages to include in the workspace.
* [`[patch]`](#the-patch-section) - Defines the patches.

An empty workspace can be created with `forc new --workspace` or `forc init --workspace`.

Expand All @@ -25,6 +26,25 @@ members = ["member1", "path/to/member2"]
The `members` field accepts entries to be given in relative path with respect to the workspace root.
Packages that are located within a workspace directory but are *not* contained within the `members` set are ignored.

## The `[patch]` section

The `[patch]` section can be used to override any dependency in the workspace dependency graph. The usage is the same with package level `[patch]` section and details can be seen [here](./manifest_reference.md#the-patch-section).
kayagokalp marked this conversation as resolved.
Show resolved Hide resolved

It is not allowed to declare patch table in member of a workspace if the workspace manifest file contains a patch table.
kayagokalp marked this conversation as resolved.
Show resolved Hide resolved

Example:

```toml
[workspace]
members = ["member1", "path/to/member2"]


[patch.'https://github.com/fuellabs/sway']
std = { git = "https://github.com/fuellabs/sway", branch = "test" }
```

In the above example each occurance of `std` as a dependency in the workspace will be changed with `std` from `test` branch of sway repo.

## Some `forc` commands that support workspaces

* `forc build` - Builds an entire workspace.
Expand Down
55 changes: 48 additions & 7 deletions forc-pkg/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,38 @@ impl PackageManifestFile {
Ok(manifest_file)
}

/// Returns an iterator over patches defined in underlying `PackageManifest` if this is a
/// standalone package.
///
/// If this package is a member of a workspace, patches are fetched from
/// the workspace manifest file.
pub fn resolve_patches(&self) -> Result<impl Iterator<Item = (String, PatchMap)>> {
let workspace_patches = self
.workspace()
.ok()
.flatten()
.and_then(|workspace| workspace.patch.clone());
let package_patches = self.patch.clone();
match (workspace_patches, package_patches) {
(Some(_), Some(_)) => bail!("Found [patch] table both in workspace and member package's manifest file. Consider removing [patch] table from package's manifest file."),
(Some(workspace_patches), None) => Ok(workspace_patches.into_iter()),
(None, Some(pkg_patches)) => Ok(pkg_patches.into_iter()),
(None, None) => Ok(BTreeMap::default().into_iter()),
}
}

/// Retrieve the listed patches for the given name from underlying `PackageManifest` if this is
/// a standalone package.
///
/// If this package is a member of a workspace, patch is fetched from
/// the workspace manifest file.
pub fn resolve_patch(&self, patch_name: &str) -> Result<Option<PatchMap>> {
Ok(self
.resolve_patches()?
.find(|(p_name, _)| patch_name == p_name.as_str())
.map(|(_, patch)| patch))
}
kayagokalp marked this conversation as resolved.
Show resolved Hide resolved

/// Read the manifest from the `Forc.toml` in the directory specified by the given `path` or
/// any of its parent directories.
///
Expand Down Expand Up @@ -509,6 +541,13 @@ impl PackageManifest {
.flat_map(|patches| patches.iter())
}

/// Retrieve the listed patches for the given name.
pub fn patch(&self, patch_name: &str) -> Option<&PatchMap> {
self.patch
.as_ref()
.and_then(|patches| patches.get(patch_name))
}

/// Check for the `core` and `std` packages under `[dependencies]`. If both are missing, add
/// `std` implicitly.
///
Expand Down Expand Up @@ -568,13 +607,6 @@ impl PackageManifest {
})
}

/// Retrieve the listed patches for the given name.
pub fn patch(&self, patch_name: &str) -> Option<&PatchMap> {
self.patch
.as_ref()
.and_then(|patches| patches.get(patch_name))
}

/// Retrieve a reference to the contract dependency with the given name.
pub fn contract_dep(&self, contract_dep_name: &str) -> Option<&ContractDependency> {
self.contract_dependencies
Expand Down Expand Up @@ -721,6 +753,7 @@ pub struct WorkspaceManifestFile {
#[serde(rename_all = "kebab-case")]
pub struct WorkspaceManifest {
workspace: Workspace,
patch: Option<BTreeMap<String, PatchMap>>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
Expand Down Expand Up @@ -826,6 +859,14 @@ impl WorkspaceManifestFile {
pub fn lock_path(&self) -> PathBuf {
self.dir().to_path_buf().join(constants::LOCK_FILE_NAME)
}

/// Produce an iterator yielding all listed patches.
pub fn patches(&self) -> impl Iterator<Item = (&String, &PatchMap)> {
self.patch
.as_ref()
.into_iter()
.flat_map(|patches| patches.iter())
}
}

impl WorkspaceManifest {
Expand Down
16 changes: 8 additions & 8 deletions forc-pkg/src/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,19 +179,19 @@ impl Source {

/// If a patch exists for this dependency source within the given project
/// manifest, this returns the patch.
fn dep_patch<'manifest>(
fn dep_patch(
&self,
dep_name: &str,
manifest: &'manifest PackageManifestFile,
) -> Option<&'manifest manifest::Dependency> {
manifest: &PackageManifestFile,
) -> Result<Option<manifest::Dependency>> {
if let Source::Git(git) = self {
if let Some(patches) = manifest.patch(&git.repo.to_string()) {
if let Some(patches) = manifest.resolve_patch(&git.repo.to_string())? {
kayagokalp marked this conversation as resolved.
Show resolved Hide resolved
if let Some(patch) = patches.get(dep_name) {
return Some(patch);
return Ok(Some(patch.clone()));
}
}
}
None
Ok(None)
}

/// If a patch exists for the dependency associated with this source within
Expand All @@ -204,8 +204,8 @@ impl Source {
manifest: &PackageManifestFile,
members: &MemberManifestFiles,
) -> Result<Self> {
match self.dep_patch(dep_name, manifest) {
Some(patch) => Self::from_manifest_dep(manifest.dir(), patch, members),
match self.dep_patch(dep_name, manifest)? {
Some(patch) => Self::from_manifest_dep(manifest.dir(), &patch, members),
None => Ok(self.clone()),
}
}
Expand Down