@@ -21,16 +21,70 @@ def load_metadata():
2121    return  json .loads (subprocess .Popen (
2222        cmd , shell = True , stdout = subprocess .PIPE ).communicate ()[0 ])
2323
24+ # Consider a situation where a crate now wants to use already existing 
25+ # developing-oriented library code for their integration tests and benchmarks, 
26+ # like creating malformed data or omitting signature verifications. Ideally, 
27+ # the code should have been guarded under the special feature 
28+ # `dev-context-only-utils` to avoid accidental misuse for production code path. 
29+ # 
30+ # In this case, the feature needs to be defined then activated for the crate 
31+ # itself. To that end, the crate actually needs to depend on itself as a 
32+ # dev-dependency with `dev-context-only-utils` activated, so that the feature 
33+ # is conditionally activated only for integration tests and benchmarks. In this 
34+ # way, other crates won't see the feature activated even if they normal-depend 
35+ # on the crate. 
36+ # 
37+ # This self-referencing dev-dependency can be thought of a variant of 
38+ # dev-dependency cycles and it's well supported by cargo. The only exception is 
39+ # when publishing. In general, cyclic dev-dependency doesn't work nicely with 
40+ # publishing: https://github.com/rust-lang/cargo/issues/4242 . 
41+ # 
42+ # However, there's a work around supported by cargo. Namely, it will ignore and 
43+ # strip these cyclic dev-dependencies when publishing, if explicit version 
44+ # isn't specified: https://github.com/rust-lang/cargo/pull/7333 (Released in 
45+ # rust 1.40.0: https://releases.rs/docs/1.40.0/#cargo ) 
46+ # 
47+ # This script follows the same safe discarding logic to exclude these 
48+ # special-cased dev dependencies from its `dependency_graph` and further 
49+ # processing. 
50+ def  is_self_dev_dep_with_dev_context_only_utils (package , dependency , wrong_self_dev_dependencies ):
51+     no_explicit_version  =  '*' 
52+ 
53+     is_special_cased  =  False 
54+     if  (dependency ['kind' ] ==  'dev'  and 
55+         dependency ['name' ] ==  package ['name' ] and 
56+         'dev-context-only-utils'  in  dependency ['features' ] and 
57+         'path'  in  dependency ):
58+         is_special_cased  =  True 
59+         if  dependency ['req' ] !=  no_explicit_version :
60+             # it's likely `{ workspace = true, ... }` is used, which implicitly pulls the 
61+             # version in... 
62+             wrong_self_dev_dependencies .append (dependency )
63+ 
64+     return  is_special_cased 
65+ 
66+ def  should_add (package , dependency , wrong_self_dev_dependencies ):
67+     related_to_solana  =  dependency ['name' ].startswith ('solana' )
68+     self_dev_dep_with_dev_context_only_utils  =  is_self_dev_dep_with_dev_context_only_utils (
69+         package , dependency , wrong_self_dev_dependencies 
70+     )
71+ 
72+     return  related_to_solana  and  not  self_dev_dep_with_dev_context_only_utils 
73+ 
2474def  get_packages ():
2575    metadata  =  load_metadata ()
2676
2777    manifest_path  =  dict ()
2878
2979    # Build dictionary of packages and their immediate solana-only dependencies 
3080    dependency_graph  =  dict ()
81+     wrong_self_dev_dependencies  =  list ()
82+ 
3183    for  pkg  in  metadata ['packages' ]:
3284        manifest_path [pkg ['name' ]] =  pkg ['manifest_path' ];
33-         dependency_graph [pkg ['name' ]] =  [x ['name' ] for  x  in  pkg ['dependencies' ] if  x ['name' ].startswith ('solana' )];
85+         dependency_graph [pkg ['name' ]] =  [
86+             x ['name' ] for  x  in  pkg ['dependencies' ] if  should_add (pkg , x , wrong_self_dev_dependencies )
87+         ];
3488
3589    # Check for direct circular dependencies 
3690    circular_dependencies  =  set ()
@@ -41,8 +95,13 @@ def get_packages():
4195
4296    for  dependency  in  circular_dependencies :
4397        sys .stderr .write ('Error: Circular dependency: {}\n ' .format (dependency ))
98+     for  dependency  in  wrong_self_dev_dependencies :
99+         sys .stderr .write ('Error: wrong dev-context-only-utils circular dependency. try: '  + 
100+             '{} = {{ path = ".", features = {} }}\n ' 
101+             .format (dependency ['name' ], json .dumps (dependency ['features' ]))
102+         )
44103
45-     if  len (circular_dependencies ) !=  0 :
104+     if  len (circular_dependencies ) !=  0   or   len ( wrong_self_dev_dependencies )  !=   0 :
46105        sys .exit (1 )
47106
48107    # Order dependencies 
0 commit comments