2525import com .google .common .collect .ImmutableList ;
2626import com .google .common .collect .ImmutableMap ;
2727import com .google .devtools .build .docgen .annot .DocumentMethods ;
28+ import com .google .devtools .build .lib .bazel .bzlmod .ModuleFileGlobals .ModuleExtensionUsageBuilder .ModuleExtensionProxy ;
2829import com .google .devtools .build .lib .bazel .bzlmod .Version .ParseException ;
2930import com .google .devtools .build .lib .cmdline .RepositoryName ;
3031import java .util .ArrayList ;
@@ -63,7 +64,7 @@ public class ModuleFileGlobals {
6364 private final boolean ignoreDevDeps ;
6465 private final Module .Builder module ;
6566 private final Map <String , ModuleKey > deps = new LinkedHashMap <>();
66- private final List <ModuleExtensionProxy > extensionProxies = new ArrayList <>();
67+ private final List <ModuleExtensionUsageBuilder > extensionUsageBuilders = new ArrayList <>();
6768 private final Map <String , ModuleOverride > overrides = new HashMap <>();
6869 private final Map <String , RepoNameUsage > repoNameUsages = new HashMap <>();
6970
@@ -375,38 +376,37 @@ public void registerToolchains(Sequence<?> toolchainLabels) throws EvalException
375376 },
376377 useStarlarkThread = true )
377378 public ModuleExtensionProxy useExtension (
378- String extensionBzlFile , String extensionName , boolean devDependency , StarlarkThread thread )
379- throws EvalException {
380- ModuleExtensionProxy newProxy =
381- new ModuleExtensionProxy ( extensionBzlFile , extensionName , thread .getCallerLocation ());
379+ String extensionBzlFile , String extensionName , boolean devDependency , StarlarkThread thread ) {
380+ ModuleExtensionUsageBuilder newUsageBuilder =
381+ new ModuleExtensionUsageBuilder (
382+ extensionBzlFile , extensionName , thread .getCallerLocation ());
382383
383384 if (ignoreDevDeps && devDependency ) {
384385 // This is a no-op proxy.
385- return newProxy ;
386+ return newUsageBuilder . getProxy ( devDependency ) ;
386387 }
387388
388- // Find an existing proxy object corresponding to this extension.
389- for (ModuleExtensionProxy proxy : extensionProxies ) {
390- if (proxy .extensionBzlFile .equals (extensionBzlFile )
391- && proxy .extensionName .equals (extensionName )) {
392- return proxy ;
389+ // Find an existing usage builder corresponding to this extension.
390+ for (ModuleExtensionUsageBuilder usageBuilder : extensionUsageBuilders ) {
391+ if (usageBuilder .extensionBzlFile .equals (extensionBzlFile )
392+ && usageBuilder .extensionName .equals (extensionName )) {
393+ return usageBuilder . getProxy ( devDependency ) ;
393394 }
394395 }
395396
396397 // If no such proxy exists, we can just use a new one.
397- extensionProxies .add (newProxy );
398- return newProxy ;
398+ extensionUsageBuilders .add (newUsageBuilder );
399+ return newUsageBuilder . getProxy ( devDependency ) ;
399400 }
400401
401- @ StarlarkBuiltin (name = "module_extension_proxy" , documented = false )
402- class ModuleExtensionProxy implements Structure {
402+ class ModuleExtensionUsageBuilder {
403403 private final String extensionBzlFile ;
404404 private final String extensionName ;
405405 private final Location location ;
406406 private final HashBiMap <String , String > imports ;
407407 private final ImmutableList .Builder <Tag > tags ;
408408
409- ModuleExtensionProxy (String extensionBzlFile , String extensionName , Location location ) {
409+ ModuleExtensionUsageBuilder (String extensionBzlFile , String extensionName , Location location ) {
410410 this .extensionBzlFile = extensionBzlFile ;
411411 this .extensionName = extensionName ;
412412 this .location = location ;
@@ -424,50 +424,69 @@ ModuleExtensionUsage buildUsage() {
424424 .build ();
425425 }
426426
427- void addImport (String localRepoName , String exportedName , Location location )
428- throws EvalException {
429- RepositoryName .validateUserProvidedRepoName (localRepoName );
430- RepositoryName .validateUserProvidedRepoName (exportedName );
431- addRepoNameUsage (localRepoName , "by a use_repo() call" , location );
432- if (imports .containsValue (exportedName )) {
433- String collisionRepoName = imports .inverse ().get (exportedName );
434- throw Starlark .errorf (
435- "The repo exported as '%s' by module extension '%s' is already imported at %s" ,
436- exportedName , extensionName , repoNameUsages .get (collisionRepoName ).getWhere ());
437- }
438- imports .put (localRepoName , exportedName );
427+ /**
428+ * Creates a proxy with the specified dev_dependency bit that shares accumulated imports and
429+ * tags with all other such proxies, thus preserving their order across dev/non-dev deps.
430+ */
431+ ModuleExtensionProxy getProxy (boolean devDependency ) {
432+ return new ModuleExtensionProxy (devDependency );
439433 }
440434
441- @ Nullable
442- @ Override
443- public Object getValue (String tagName ) throws EvalException {
444- return new StarlarkValue () {
445- @ StarlarkMethod (
446- name = "call" ,
447- selfCall = true ,
448- documented = false ,
449- extraKeywords = @ Param (name = "kwargs" ),
450- useStarlarkThread = true )
451- public void call (Dict <String , Object > kwargs , StarlarkThread thread ) {
452- tags .add (
453- Tag .builder ()
454- .setTagName (tagName )
455- .setAttributeValues (kwargs )
456- .setLocation (thread .getCallerLocation ())
457- .build ());
435+ @ StarlarkBuiltin (name = "module_extension_proxy" , documented = false )
436+ class ModuleExtensionProxy implements Structure {
437+
438+ private final boolean devDependency ;
439+
440+ private ModuleExtensionProxy (boolean devDependency ) {
441+ this .devDependency = devDependency ;
442+ }
443+
444+ void addImport (String localRepoName , String exportedName , Location location )
445+ throws EvalException {
446+ RepositoryName .validateUserProvidedRepoName (localRepoName );
447+ RepositoryName .validateUserProvidedRepoName (exportedName );
448+ addRepoNameUsage (localRepoName , "by a use_repo() call" , location );
449+ if (imports .containsValue (exportedName )) {
450+ String collisionRepoName = imports .inverse ().get (exportedName );
451+ throw Starlark .errorf (
452+ "The repo exported as '%s' by module extension '%s' is already imported at %s" ,
453+ exportedName , extensionName , repoNameUsages .get (collisionRepoName ).getWhere ());
458454 }
459- } ;
460- }
455+ imports . put ( localRepoName , exportedName ) ;
456+ }
461457
462- @ Override
463- public ImmutableCollection <String > getFieldNames () {
464- return ImmutableList .of ();
465- }
458+ @ Nullable
459+ @ Override
460+ public Object getValue (String tagName ) throws EvalException {
461+ return new StarlarkValue () {
462+ @ StarlarkMethod (
463+ name = "call" ,
464+ selfCall = true ,
465+ documented = false ,
466+ extraKeywords = @ Param (name = "kwargs" ),
467+ useStarlarkThread = true )
468+ public void call (Dict <String , Object > kwargs , StarlarkThread thread ) {
469+ tags .add (
470+ Tag .builder ()
471+ .setTagName (tagName )
472+ .setAttributeValues (kwargs )
473+ .setDevDependency (devDependency )
474+ .setLocation (thread .getCallerLocation ())
475+ .build ());
476+ }
477+ };
478+ }
466479
467- @ Nullable
468- @ Override
469- public String getErrorMessageForUnknownField (String field ) {
470- return null ;
480+ @ Override
481+ public ImmutableCollection <String > getFieldNames () {
482+ return ImmutableList .of ();
483+ }
484+
485+ @ Nullable
486+ @ Override
487+ public String getErrorMessageForUnknownField (String field ) {
488+ return null ;
489+ }
471490 }
472491 }
473492
@@ -824,8 +843,8 @@ public Module buildModule() {
824843 .setDeps (ImmutableMap .copyOf (deps ))
825844 .setOriginalDeps (ImmutableMap .copyOf (deps ))
826845 .setExtensionUsages (
827- extensionProxies .stream ()
828- .map (ModuleExtensionProxy ::buildUsage )
846+ extensionUsageBuilders .stream ()
847+ .map (ModuleExtensionUsageBuilder ::buildUsage )
829848 .collect (toImmutableList ()))
830849 .build ();
831850 }
0 commit comments