Repository: ant-ivy Updated Branches: refs/heads/master 0712acabd -> b3f7c8596
NEW: The buildlist task can now have `root` and `leaf` subelements to specify the organisation (and other attributes) of the root and leaf modules Project: http://git-wip-us.apache.org/repos/asf/ant-ivy/repo Commit: http://git-wip-us.apache.org/repos/asf/ant-ivy/commit/b3f7c859 Tree: http://git-wip-us.apache.org/repos/asf/ant-ivy/tree/b3f7c859 Diff: http://git-wip-us.apache.org/repos/asf/ant-ivy/diff/b3f7c859 Branch: refs/heads/master Commit: b3f7c8596957c87934f7d90e5e5cb94877df7d1a Parents: 0712aca Author: Maarten Coene <maart...@apache.org> Authored: Sun Sep 16 23:43:30 2018 +0200 Committer: Maarten Coene <maart...@apache.org> Committed: Sun Sep 16 23:43:30 2018 +0200 ---------------------------------------------------------------------- asciidoc/release-notes.adoc | 1 + asciidoc/use/buildlist.adoc | 51 ++++- src/java/org/apache/ivy/ant/IvyBuildList.java | 217 ++++++++++++++++----- 3 files changed, 217 insertions(+), 52 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/b3f7c859/asciidoc/release-notes.adoc ---------------------------------------------------------------------- diff --git a/asciidoc/release-notes.adoc b/asciidoc/release-notes.adoc index efa7057..dbae920 100644 --- a/asciidoc/release-notes.adoc +++ b/asciidoc/release-notes.adoc @@ -101,6 +101,7 @@ For details about the following changes, check our JIRA install at link:https:// - NEW: Add ivy.maven.lookup.sources and ivy.maven.lookup.javadoc variables to control the lookup of the additional artifacts. Defaults to true, for backward compatibility (jira:IVY-1529[]) - NEW: Add (conditional) support for SHA-256, SHA-512 and SHA-384 checksum algorithms (jira:IVY-1554[]) (Thanks to Jaikiran Pai) - NEW: The standalone Ivy jar can now be used to generate a pom file for the resolved module, using the `makepom` option (Thanks to link:https://github.com/aanno[Thomas Pasch]) +- NEW: The buildlist task can now have `root` and `leaf` subelements to specify the organisation (and other attributes) of the root and leaf modules (jira:/IVY-1242[]) and (jira:IVY-1293[]) //// http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/b3f7c859/asciidoc/use/buildlist.adoc ---------------------------------------------------------------------- diff --git a/asciidoc/use/buildlist.adoc b/asciidoc/use/buildlist.adoc index 4d827eb..120757e 100644 --- a/asciidoc/use/buildlist.adoc +++ b/asciidoc/use/buildlist.adoc @@ -32,9 +32,9 @@ When the `ivy.xml` of the modules that you want to order contains link:../ivyfil (*__since 1.4__*) The `ivy.sorted.modules` property is set in Ant project at the end of the task with a comma separated list of ordered modules. This can be useful for debug or information purpose. -[*__since 2.0__*] +(*__since 2.0__*) The `root` and `leaf` attributes can be a delimited list of modules to use as roots. These modules, and all their dependencies will be included in the build list. -The `root` and `leaf` attributes can be a delimited list of modules to use as roots. These modules, and all their dependencies will be included in the build list. +(*__since 2.5__*) The root and leaf modules can also be specified as nested `root` and `leaf` elements. This way, not only the module name can be specified, but also the organisation, revision and branch of the root/leaf. By default, all the modules included in a circular dependency are grouped together so that any dependency of any module in the loop will appear before the modules in the loop. This guarantees that if there is a dependency path between a module A and a module B (but no dependency path from B to A), B will always appear before A even if A is included in a loop in the provided set of modules to sort. Note that a circular dependency can also trigger a failure depending on the value configured in the `circularDependencyStrategy` of your link:../settings/conf{outfilesuffix}#circularDependencyStrategy[settings] @@ -76,6 +76,29 @@ This field is ignored when neither root nor leaf is filled.|No. Defaults to no ` |settingsRef|(*__since 2.0__*) A reference to Ivy settings that must be used by this task|No, `ivy.instance` is taken by default. |======= +== Child elements + +[options="header",cols="15%,50%,35%"] +|======= +|Element|Description|Cardinality +|root|(*__since 2.5__*) Declares a root module. + +This element takes the following attributes: + +* `organisation`: the organisation of the root module (defaults to *) + +* `module`: the name of the root module (defaults to *) + +* `revision`: the revision of the root module (defaults to *) + +* `branch`: the branch of the root module (default to *) +* `file`: a specific ivy.xml file to use as root module|0..n +|leaf|(*__since 2.5__*) Declares a leaf module. + +This element takes the following attributes: + +* `organisation`: the organisation of the leaf module (defaults to *) + +* `module`: the name of the leaf module (defaults to *) + +* `revision`: the revision of the leaf module (defaults to *) + +* `branch`: the branch of the leaf module (default to *) +* `file`: a specific ivy.xml file to use as leaf module|0..n +|======= + == Parameters specified as nested elements === fileset @@ -132,3 +155,27 @@ Builds a list of `build.xml` files sorted according to the `ivy.xml` files found ---- Builds a list of `build.xml` files sorted according to the `ivy.xml` files found in an Ivy directory relative to those build files. Only `build.xml` files of modules which have dependencies (direct or transitive) on `mymodule` are put in the result list. + +''' + +[source,xml] +---- + <ivy:buildlist reference="build-path" ivyfilepath="ivy/ivy.xml"> + <root organisation="myorg" module="myapp" /> + <fileset dir="projects" includes="**/build.xml"/> + </ivy:buildlist> +---- + +Builds a list of `build.xml` files sorted according to the `ivy.xml` files found in an Ivy directory relative to those build files. Only `build.xml` files of modules which are dependencies of `myorg#myapp` (either direct or transitive) are put in the result list. + +''' + +[source,xml] +---- + <ivy:buildlist reference="build-path" ivyfilepath="ivy/ivy.xml"> + <root file="/path/to/myapp-ivy.xml" /> + <fileset dir="projects" includes="**/build.xml"/> + </ivy:buildlist> +---- + +Builds a list of `build.xml` files sorted according to the `ivy.xml` files found in an Ivy directory relative to those build files. Only `build.xml` files of modules which are dependencies defined in `/path/to/myapp-ivy.xml` (either direct or transitive) are put in the result list. http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/b3f7c859/src/java/org/apache/ivy/ant/IvyBuildList.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/ivy/ant/IvyBuildList.java b/src/java/org/apache/ivy/ant/IvyBuildList.java index 8340065..87c7054 100644 --- a/src/java/org/apache/ivy/ant/IvyBuildList.java +++ b/src/java/org/apache/ivy/ant/IvyBuildList.java @@ -30,11 +30,14 @@ import java.util.Set; import java.util.StringTokenizer; import org.apache.ivy.Ivy; +import org.apache.ivy.core.IvyPatternHelper; import org.apache.ivy.core.module.descriptor.DependencyDescriptor; import org.apache.ivy.core.module.descriptor.ModuleDescriptor; import org.apache.ivy.core.module.id.ModuleId; import org.apache.ivy.core.settings.IvySettings; import org.apache.ivy.core.sort.SortOptions; +import org.apache.ivy.plugins.matcher.MapMatcher; +import org.apache.ivy.plugins.matcher.PatternMatcher; import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry; import org.apache.ivy.util.Message; import org.apache.tools.ant.BuildException; @@ -62,6 +65,60 @@ public class IvyBuildList extends IvyTask { } } + public static final class BuildListModule { + + private String organisation; + + private String module; + + private String revision; + + private String branch; + + private File file; + + public String getOrganisation() { + return organisation; + } + + public void setOrganisation(String organisation) { + this.organisation = organisation; + } + + public String getModule() { + return module; + } + + public void setModule(String module) { + this.module = module; + } + + public String getRevision() { + return revision; + } + + public void setRevision(String revision) { + this.revision = revision; + } + + public String getBranch() { + return branch; + } + + public void setBranch(String branch) { + this.branch = branch; + } + + public File getFile() { + return file; + } + + public void setFile(File file) { + this.file = file; + } + + } + public static final String DESCRIPTOR_REQUIRED = "required"; private List<FileSet> buildFileSets = new ArrayList<>(); @@ -78,10 +135,14 @@ public class IvyBuildList extends IvyTask { private String root = "*"; + private List<BuildListModule> roots = new ArrayList<>(); + private boolean excludeRoot = false; private String leaf = "*"; + private List<BuildListModule> leafs = new ArrayList<>(); + private String delimiter = ","; private boolean excludeLeaf = false; @@ -110,6 +171,12 @@ public class IvyBuildList extends IvyTask { this.root = root; } + public BuildListModule createRoot() { + BuildListModule root = new BuildListModule(); + roots.add(root); + return root; + } + public boolean isExcludeRoot() { return excludeRoot; } @@ -126,6 +193,12 @@ public class IvyBuildList extends IvyTask { this.leaf = leaf; } + public BuildListModule createLeaf() { + BuildListModule leaf = new BuildListModule(); + leafs.add(leaf); + return leaf; + } + public boolean isExcludeLeaf() { return excludeLeaf; } @@ -172,28 +245,9 @@ public class IvyBuildList extends IvyTask { List<File> noDescriptor = new ArrayList<>(); Collection<ModuleDescriptor> mds = new ArrayList<>(); - Set<String> rootModuleNames = new LinkedHashSet<>(); - if (!"*".equals(root)) { - StringTokenizer st = new StringTokenizer(root, delimiter); - while (st.hasMoreTokens()) { - rootModuleNames.add(st.nextToken()); - } - } - - Set<String> leafModuleNames = new LinkedHashSet<>(); - if (!"*".equals(leaf)) { - StringTokenizer st = new StringTokenizer(leaf, delimiter); - while (st.hasMoreTokens()) { - leafModuleNames.add(st.nextToken()); - } - } - - Set<String> restartFromModuleNames = new LinkedHashSet<>(); - if (!"*".equals(restartFrom)) { - StringTokenizer st = new StringTokenizer(restartFrom, delimiter); - // Only accept one (first) module - restartFromModuleNames.add(st.nextToken()); - } + Set<MapMatcher> rootModules = convert(roots, root, settings); + Set<MapMatcher> leafModules = convert(leafs, leaf, settings); + Set<MapMatcher> restartFromModules = convert(Collections.<BuildListModule>emptyList(), restartFrom, settings); for (FileSet fs : buildFileSets) { DirectoryScanner ds = fs.getDirectoryScanner(getProject()); @@ -225,19 +279,19 @@ public class IvyBuildList extends IvyTask { } } - List<ModuleDescriptor> leafModuleDescriptors = convertModuleNamesToModuleDescriptors(mds, - leafModuleNames, "leaf"); - List<ModuleDescriptor> rootModuleDescriptors = convertModuleNamesToModuleDescriptors(mds, - rootModuleNames, "root"); - List<ModuleDescriptor> restartFromModuleDescriptors = convertModuleNamesToModuleDescriptors( - mds, restartFromModuleNames, "restartFrom"); + List<ModuleDescriptor> leafModuleDescriptors = + findModuleDescriptors(mds, leafModules, "leaf"); + List<ModuleDescriptor> rootModuleDescriptors = + findModuleDescriptors(mds, rootModules, "root"); + List<ModuleDescriptor> restartFromModuleDescriptors = + findModuleDescriptors(mds, restartFromModules, "restartFrom"); if (!rootModuleDescriptors.isEmpty()) { - Message.info("Filtering modules based on roots " + rootModuleNames); + Message.info("Filtering modules based on roots [" + extractModuleNames(rootModules) + "]"); mds = filterModulesFromRoot(mds, rootModuleDescriptors); } if (!leafModuleDescriptors.isEmpty()) { - Message.info("Filtering modules based on leafs " + leafModuleNames); + Message.info("Filtering modules based on leafs [" + extractModuleNames(leafModules) + "]"); mds = filterModulesFromLeaf(mds, leafModuleDescriptors); } @@ -260,6 +314,7 @@ public class IvyBuildList extends IvyTask { if (!restartFromModuleDescriptors.isEmpty()) { boolean foundRestartFrom = false; List<ModuleDescriptor> keptModules = new ArrayList<>(); + // Only accept one (first) module ModuleDescriptor restartFromModuleDescriptor = restartFromModuleDescriptors.get(0); for (ModuleDescriptor md : sortedModules) { if (md.equals(restartFromModuleDescriptor)) { @@ -289,6 +344,54 @@ public class IvyBuildList extends IvyTask { getProject().setProperty("ivy.sorted.modules", order.toString()); } + private Set<MapMatcher> convert(List<BuildListModule> modulesList, String modulesString, IvySettings settings) { + Set<MapMatcher> result = new LinkedHashSet<>(); + + for (BuildListModule module : modulesList) { + File ivyFile = module.getFile(); + if (ivyFile == null) { + String org = module.getOrganisation(); + String name = module.getModule(); + String rev = module.getRevision(); + String branch = module.getBranch(); + + Map<String, String> attributes = new HashMap<>(); + attributes.put(IvyPatternHelper.ORGANISATION_KEY, org == null ? PatternMatcher.ANY_EXPRESSION : org); + attributes.put(IvyPatternHelper.MODULE_KEY, name == null ? PatternMatcher.ANY_EXPRESSION : name); + attributes.put(IvyPatternHelper.MODULE_KEY, rev == null ? PatternMatcher.ANY_EXPRESSION : rev); + attributes.put(IvyPatternHelper.MODULE_KEY, branch == null ? PatternMatcher.ANY_EXPRESSION : branch); + + result.add(new MapMatcher(attributes, settings.getMatcher(PatternMatcher.EXACT))); + } else { + try { + ModuleDescriptor md = ModuleDescriptorParserRegistry.getInstance() + .parseDescriptor(settings, ivyFile.toURI().toURL(), + doValidate(settings)); + + Map<String, String> attributes = new HashMap<>(); + attributes.putAll(md.getModuleRevisionId().getAttributes()); + attributes.put("resource", md.getResource().getName()); + + result.add(new MapMatcher(attributes, settings.getMatcher(PatternMatcher.EXACT))); + } catch (Exception e) { + throw new BuildException(e); + } + } + } + + if (!"*".equals(modulesString)) { + StringTokenizer st = new StringTokenizer(modulesString, getDelimiter()); + while (st.hasMoreTokens()) { + Map<String, String> attributes = new HashMap<>(); + attributes.put(IvyPatternHelper.MODULE_KEY, st.nextToken()); + + result.add(new MapMatcher(attributes, settings.getMatcher(PatternMatcher.EXACT))); + } + } + + return result; + } + private void onMissingDescriptor(File buildFile, File ivyFile, List<File> noDescriptor) { switch (onMissingDescriptor) { case OnMissingDescriptor.FAIL: @@ -312,38 +415,52 @@ public class IvyBuildList extends IvyTask { } } - private List<ModuleDescriptor> convertModuleNamesToModuleDescriptors( - Collection<ModuleDescriptor> mds, Set<String> moduleNames, String kind) { + private List<ModuleDescriptor> findModuleDescriptors( + Collection<ModuleDescriptor> mds, Set<MapMatcher> matchers, String kind) { List<ModuleDescriptor> result = new ArrayList<>(); - Set<String> foundModuleNames = new HashSet<>(); + Set<MapMatcher> missingMatchers = new HashSet<>(matchers); for (ModuleDescriptor md : mds) { - String name = md.getModuleRevisionId().getModuleId().getName(); - if (moduleNames.contains(name)) { - foundModuleNames.add(name); - result.add(md); + Map<String, String> attributes = new HashMap<>(); + attributes.putAll(md.getAttributes()); + attributes.put("resource", md.getResource().getName()); + + for (MapMatcher matcher : matchers) { + if (matcher.matches(attributes)) { + missingMatchers.remove(matcher); + result.add(md); + } } } - if (foundModuleNames.size() < moduleNames.size()) { - Set<String> missingModules = new HashSet<>(moduleNames); - missingModules.removeAll(foundModuleNames); - - StringBuilder missingNames = new StringBuilder(); - String sep = ""; - for (String name : missingModules) { - missingNames.append(sep); - missingNames.append(name); - sep = ", "; - } - + if (!missingMatchers.isEmpty()) { throw new BuildException("unable to find " + kind + " module(s) " - + missingNames.toString() + " in build fileset"); + + extractModuleNames(missingMatchers) + " in build fileset"); } return result; } + private String extractModuleNames(Set<MapMatcher> matchers) { + StringBuilder result = new StringBuilder(); + + String sep = ""; + for (MapMatcher matcher : matchers) { + result.append(sep); + + Map<String, String> attributes = matcher.getAttributes(); + String organisation = attributes.get(IvyPatternHelper.ORGANISATION_KEY); + if (organisation != null && !PatternMatcher.ANY_EXPRESSION.equals(organisation)) { + result.append(organisation); + result.append('#'); + } + result.append(attributes.get(IvyPatternHelper.MODULE_KEY)); + sep = ", "; + } + + return result.toString(); + } + /** * Returns a collection of ModuleDescriptors that are contained in the input collection of * ModuleDescriptors and upon which the root module depends