@@ -10,7 +10,9 @@ use crate::AlreadyPrintedError;
1010use anyhow:: { anyhow, bail, Context as _} ;
1111use cargo_platform:: Platform ;
1212use cargo_util:: paths:: { self , normalize_path} ;
13- use cargo_util_schemas:: manifest:: { self , TomlManifest } ;
13+ use cargo_util_schemas:: manifest:: {
14+ self , PackageName , PathBaseName , TomlDependency , TomlDetailedDependency , TomlManifest ,
15+ } ;
1416use cargo_util_schemas:: manifest:: { RustVersion , StringOrBool } ;
1517use itertools:: Itertools ;
1618use lazycell:: LazyCell ;
@@ -296,7 +298,7 @@ fn normalize_toml(
296298 features : None ,
297299 target : None ,
298300 replace : original_toml. replace . clone ( ) ,
299- patch : original_toml . patch . clone ( ) ,
301+ patch : None ,
300302 workspace : original_toml. workspace . clone ( ) ,
301303 badges : None ,
302304 lints : None ,
@@ -310,6 +312,7 @@ fn normalize_toml(
310312 inherit_cell
311313 . try_borrow_with ( || load_inheritable_fields ( gctx, manifest_file, & workspace_config) )
312314 } ;
315+ let workspace_root = || inherit ( ) . map ( |fields| fields. ws_root ( ) ) ;
313316
314317 if let Some ( original_package) = original_toml. package ( ) {
315318 let package_name = & original_package. name ;
@@ -390,6 +393,7 @@ fn normalize_toml(
390393 & activated_opt_deps,
391394 None ,
392395 & inherit,
396+ & workspace_root,
393397 package_root,
394398 warnings,
395399 ) ?;
@@ -410,6 +414,7 @@ fn normalize_toml(
410414 & activated_opt_deps,
411415 Some ( DepKind :: Development ) ,
412416 & inherit,
417+ & workspace_root,
413418 package_root,
414419 warnings,
415420 ) ?;
@@ -430,6 +435,7 @@ fn normalize_toml(
430435 & activated_opt_deps,
431436 Some ( DepKind :: Build ) ,
432437 & inherit,
438+ & workspace_root,
433439 package_root,
434440 warnings,
435441 ) ?;
@@ -443,6 +449,7 @@ fn normalize_toml(
443449 & activated_opt_deps,
444450 None ,
445451 & inherit,
452+ & workspace_root,
446453 package_root,
447454 warnings,
448455 ) ?;
@@ -463,6 +470,7 @@ fn normalize_toml(
463470 & activated_opt_deps,
464471 Some ( DepKind :: Development ) ,
465472 & inherit,
473+ & workspace_root,
466474 package_root,
467475 warnings,
468476 ) ?;
@@ -483,6 +491,7 @@ fn normalize_toml(
483491 & activated_opt_deps,
484492 Some ( DepKind :: Build ) ,
485493 & inherit,
494+ & workspace_root,
486495 package_root,
487496 warnings,
488497 ) ?;
@@ -499,6 +508,13 @@ fn normalize_toml(
499508 }
500509 normalized_toml. target = ( !normalized_target. is_empty ( ) ) . then_some ( normalized_target) ;
501510
511+ normalized_toml. patch = normalize_patch (
512+ gctx,
513+ original_toml. patch . as_ref ( ) ,
514+ & workspace_root,
515+ features,
516+ ) ?;
517+
502518 let normalized_lints = original_toml
503519 . lints
504520 . clone ( )
@@ -519,6 +535,37 @@ fn normalize_toml(
519535 Ok ( normalized_toml)
520536}
521537
538+ fn normalize_patch < ' a > (
539+ gctx : & GlobalContext ,
540+ original_patch : Option < & BTreeMap < String , BTreeMap < PackageName , TomlDependency > > > ,
541+ workspace_root : & dyn Fn ( ) -> CargoResult < & ' a PathBuf > ,
542+ features : & Features ,
543+ ) -> CargoResult < Option < BTreeMap < String , BTreeMap < PackageName , TomlDependency > > > > {
544+ if let Some ( patch) = original_patch {
545+ let mut normalized_patch = BTreeMap :: new ( ) ;
546+ for ( name, packages) in patch {
547+ let mut normalized_packages = BTreeMap :: new ( ) ;
548+ for ( pkg, dep) in packages {
549+ let dep = if let TomlDependency :: Detailed ( dep) = dep {
550+ let mut dep = dep. clone ( ) ;
551+ normalize_path_dependency ( gctx, & mut dep, workspace_root, features)
552+ . with_context ( || {
553+ format ! ( "resolving path for patch of ({pkg}) for source ({name})" )
554+ } ) ?;
555+ TomlDependency :: Detailed ( dep)
556+ } else {
557+ dep. clone ( )
558+ } ;
559+ normalized_packages. insert ( pkg. clone ( ) , dep) ;
560+ }
561+ normalized_patch. insert ( name. clone ( ) , normalized_packages) ;
562+ }
563+ Ok ( Some ( normalized_patch) )
564+ } else {
565+ Ok ( None )
566+ }
567+ }
568+
522569#[ tracing:: instrument( skip_all) ]
523570fn normalize_package_toml < ' a > (
524571 original_package : & manifest:: TomlPackage ,
@@ -710,6 +757,7 @@ fn normalize_dependencies<'a>(
710757 activated_opt_deps : & HashSet < & str > ,
711758 kind : Option < DepKind > ,
712759 inherit : & dyn Fn ( ) -> CargoResult < & ' a InheritableFields > ,
760+ workspace_root : & dyn Fn ( ) -> CargoResult < & ' a PathBuf > ,
713761 package_root : & Path ,
714762 warnings : & mut Vec < String > ,
715763) -> CargoResult < Option < BTreeMap < manifest:: PackageName , manifest:: InheritableDependency > > > {
@@ -768,6 +816,8 @@ fn normalize_dependencies<'a>(
768816 }
769817 }
770818 }
819+ normalize_path_dependency ( gctx, d, workspace_root, features)
820+ . with_context ( || format ! ( "resolving path dependency {name_in_toml}" ) ) ?;
771821 }
772822
773823 // if the dependency is not optional, it is always used
@@ -786,6 +836,23 @@ fn normalize_dependencies<'a>(
786836 Ok ( Some ( deps) )
787837}
788838
839+ fn normalize_path_dependency < ' a > (
840+ gctx : & GlobalContext ,
841+ detailed_dep : & mut TomlDetailedDependency ,
842+ workspace_root : & dyn Fn ( ) -> CargoResult < & ' a PathBuf > ,
843+ features : & Features ,
844+ ) -> CargoResult < ( ) > {
845+ if let Some ( base) = detailed_dep. base . take ( ) {
846+ if let Some ( path) = detailed_dep. path . as_mut ( ) {
847+ let new_path = lookup_path_base ( & base, gctx, workspace_root, features) ?. join ( & path) ;
848+ * path = new_path. to_str ( ) . unwrap ( ) . to_string ( ) ;
849+ } else {
850+ bail ! ( "`base` can only be used with path dependencies" ) ;
851+ }
852+ }
853+ Ok ( ( ) )
854+ }
855+
789856fn load_inheritable_fields (
790857 gctx : & GlobalContext ,
791858 normalized_path : & Path ,
@@ -901,13 +968,17 @@ impl InheritableFields {
901968 } ;
902969 let mut dep = dep. clone ( ) ;
903970 if let manifest:: TomlDependency :: Detailed ( detailed) = & mut dep {
904- if let Some ( rel_path) = & detailed. path {
905- detailed. path = Some ( resolve_relative_path (
906- name,
907- self . ws_root ( ) ,
908- package_root,
909- rel_path,
910- ) ?) ;
971+ if detailed. base . is_none ( ) {
972+ // If this is a path dependency without a base, then update the path to be relative
973+ // to the workspace root instead.
974+ if let Some ( rel_path) = & detailed. path {
975+ detailed. path = Some ( resolve_relative_path (
976+ name,
977+ self . ws_root ( ) ,
978+ package_root,
979+ rel_path,
980+ ) ?) ;
981+ }
911982 }
912983 }
913984 Ok ( dep)
@@ -2151,6 +2222,33 @@ fn to_dependency_source_id<P: ResolveToPath + Clone>(
21512222 }
21522223}
21532224
2225+ pub ( crate ) fn lookup_path_base < ' a > (
2226+ base : & PathBaseName ,
2227+ gctx : & GlobalContext ,
2228+ workspace_root : & dyn Fn ( ) -> CargoResult < & ' a PathBuf > ,
2229+ features : & Features ,
2230+ ) -> CargoResult < PathBuf > {
2231+ features. require ( Feature :: path_bases ( ) ) ?;
2232+
2233+ // HACK: The `base` string is user controlled, but building the path is safe from injection
2234+ // attacks since the `PathBaseName` type restricts the characters that can be used to exclude `.`
2235+ let base_key = format ! ( "path-bases.{base}" ) ;
2236+
2237+ // Look up the relevant base in the Config and use that as the root.
2238+ if let Some ( path_bases) = gctx. get :: < Option < ConfigRelativePath > > ( & base_key) ? {
2239+ Ok ( path_bases. resolve_path ( gctx) )
2240+ } else {
2241+ // Otherwise, check the built-in bases.
2242+ match base. as_str ( ) {
2243+ "workspace" => Ok ( workspace_root ( ) ?. clone ( ) ) ,
2244+ _ => bail ! (
2245+ "path base `{base}` is undefined. \
2246+ You must add an entry for `{base}` in the Cargo configuration [path-bases] table."
2247+ ) ,
2248+ }
2249+ }
2250+ }
2251+
21542252pub trait ResolveToPath {
21552253 fn resolve ( & self , gctx : & GlobalContext ) -> PathBuf ;
21562254}
@@ -2865,6 +2963,7 @@ fn prepare_toml_for_publish(
28652963 let mut d = d. clone ( ) ;
28662964 // Path dependencies become crates.io deps.
28672965 d. path . take ( ) ;
2966+ d. base . take ( ) ;
28682967 // Same with git dependencies.
28692968 d. git . take ( ) ;
28702969 d. branch . take ( ) ;
0 commit comments