4141import org .eclipse .equinox .p2 .core .IProvisioningAgent ;
4242import org .eclipse .equinox .p2 .core .ProvisionException ;
4343import org .eclipse .equinox .p2 .metadata .IInstallableUnit ;
44+ import org .eclipse .equinox .p2 .metadata .IRequirement ;
4445import org .eclipse .equinox .p2 .metadata .Version ;
4546import org .eclipse .equinox .p2 .query .QueryUtil ;
4647import org .eclipse .equinox .p2 .repository .metadata .IMetadataRepository ;
4748import org .eclipse .equinox .p2 .repository .metadata .IMetadataRepositoryManager ;
49+ import org .eclipse .tycho .core .MarkdownBuilder ;
4850import org .eclipse .tycho .p2resolver .TargetDefinitionVariableResolver ;
4951import org .eclipse .tycho .targetplatform .TargetDefinition ;
5052
5658@ Named
5759public class InstallableUnitLocationUpdater {
5860
61+ private static final String EMPTY_VERSION = "0.0.0" ;
5962 private static final Pattern DEFAULT_VERSION_PATTERN = Pattern .compile ("(\\ d+)\\ .(\\ d+)" );
6063 private static final Pattern DEFAULT_DATE_PATTERN = Pattern .compile ("(\\ d{4}-\\ d{2})" );
6164 private static final Period DEFAULT_PERIOD = Period .ofMonths (3 ).plusDays (7 );
@@ -69,43 +72,88 @@ public class InstallableUnitLocationUpdater {
6972
7073 boolean update (Element iuLocation , UpdateTargetMojo context )
7174 throws MojoFailureException , URISyntaxException , ProvisionException {
75+ Log log = context .getLog ();
76+ ResolvedRepository location = getResolvedLocation (iuLocation );
77+ log .info ("Check " + location .getLocation () + " for updates..." );
7278 List <IU > units = iuLocation .getChildren ("unit" ).stream ()
73- .map (unit -> new IU (unit .getAttributeValue ("id" ), unit .getAttributeValue ("version" ), unit )).toList ();
74- IMetadataRepository repository = getMetadataRepository (iuLocation , context , units );
75- boolean updated = false ;
79+ .map (unit -> new IU (unit .getAttributeValue ("id" ), getUnitVersion (unit ), unit )).toList ();
80+ IMetadataRepositoryManager repositoryManager = agent .getService (IMetadataRepositoryManager .class );
81+ URI currentLocation = new URI (location .location ());
82+ IMetadataRepository currentRepository = null ;
83+ MetadataRepositoryUpdate updateRepository = getMetadataRepository (location , context , units , repositoryManager );
84+ boolean updated = updateRepository .updateLocation (location );
85+ List <VersionUpdate > updates = new ArrayList <>();
7686 for (Element unit : iuLocation .getChildren ("unit" )) {
7787 String id = unit .getAttributeValue ("id" );
78- IInstallableUnit latestUnit = repository
88+ IInstallableUnit latestUnit = updateRepository . update ()
7989 .query (QueryUtil .createLatestQuery (QueryUtil .createIUQuery (id )), null ).stream ().findFirst ()
8090 .orElse (null );
8191 if (latestUnit != null ) {
92+ String currentVersion = getUnitVersion (unit );
93+ if (EMPTY_VERSION .equals (currentVersion ) && !context .isUpdateEmptyVersion ()) {
94+ continue ;
95+ }
8296 String newVersion = latestUnit .getVersion ().toString ();
83- String currentVersion = unit .getAttributeValue ("version" );
8497 if (newVersion .equals (currentVersion )) {
85- context . getLog () .debug ("unit '" + id + "' is already up-to date" );
98+ log .debug ("unit '" + id + "' is already up-to date" );
8699 } else {
87- updated = true ;
88- context .getLog ()
89- .info ("Update version of unit '" + id + "' from " + currentVersion + " > " + newVersion );
90- unit .setAttribute ("version" , newVersion );
100+ if (currentRepository == null ) {
101+ currentRepository = repositoryManager .loadRepository (currentLocation , null );
102+ }
103+ IInstallableUnit currentIU = currentRepository
104+ .query (QueryUtil .createIUQuery (id , Version .create (currentVersion )), null ).stream ()
105+ .findFirst ().orElse (null );
106+ VersionUpdate update = new VersionUpdate (id , currentVersion , currentIU , latestUnit );
107+ if (update .hasVersionChange () && isCompatibleChange (update , context )) {
108+ log .info ("Update version of unit '" + id + "' from " + update .getPreviousVersion () + " > "
109+ + newVersion );
110+ updates .add (update );
111+ updated = true ;
112+ unit .setAttribute ("version" , newVersion );
113+ }
91114 }
92115 } else {
93- context .getLog ().warn (
94- "Resolution result does not contain root installable unit '" + id + "' update is skipped!" );
116+ log .warn ("Resolution result does not contain root installable unit '" + id + "' update is skipped!" );
117+ }
118+ }
119+ MarkdownBuilder builder = context .builder ;
120+ if (updates .isEmpty ()) {
121+ if (updateRepository .hasUpdate (currentLocation )) {
122+ builder .h3 ("The location " + location .location () + " was updated:" );
123+ builder .addListItem ("Location changed to " + updateRepository .uri ());
124+ builder .newLine ();
95125 }
126+ return updated ;
127+ }
128+ if (updateRepository .hasUpdate (currentLocation )) {
129+ builder .h3 ("The location " + location .location () + " was updated:" );
130+ builder .addListItem ("Location changed to " + updateRepository .uri ());
131+ } else {
132+ builder .h3 ("The content of the location " + location .location () + " was updated:" );
133+ }
134+ for (VersionUpdate versionUpdate : updates ) {
135+ describeUpdate (versionUpdate , builder );
96136 }
137+ builder .newLine ();
97138 return updated ;
98139 }
99140
100- private IMetadataRepository getMetadataRepository (Element iuLocation , UpdateTargetMojo context , List <IU > units )
141+ private String getUnitVersion (Element unit ) {
142+ String value = unit .getAttributeValue ("version" );
143+ if (value == null || value .isBlank ()) {
144+ return EMPTY_VERSION ;
145+ }
146+ return value ;
147+ }
148+
149+ private MetadataRepositoryUpdate getMetadataRepository (ResolvedRepository location , UpdateTargetMojo context ,
150+ List <IU > units , IMetadataRepositoryManager repositoryManager )
101151 throws URISyntaxException , ProvisionException {
102- ResolvedRepository location = getResolvedLocation (iuLocation );
103- String raw = location .getLocation ();
104152 Log log = context .getLog ();
105- log .debug ("Look for updates of location " + raw );
153+ log .debug ("Look for updates of location " + location .getLocation ());
154+ String raw = location .getLocation ();
106155 URI uri = new URI (raw );
107156 List <String > discovery = context .getUpdateSiteDiscovery ();
108- IMetadataRepositoryManager repositoryManager = agent .getService (IMetadataRepositoryManager .class );
109157 for (String strategy : discovery ) {
110158 String trim = strategy .trim ();
111159 if (trim .equals ("parent" )) {
@@ -122,7 +170,7 @@ private IMetadataRepository getMetadataRepository(Element iuLocation, UpdateTarg
122170 log .debug ("No parent repository found for location " + uri + " using " + parentURI + ": " + e );
123171 continue ;
124172 }
125- IMetadataRepository bestLocation = findBestLocation (context , units , location , repositoryManager ,
173+ MetadataRepositoryUpdate bestLocation = findBestLocation (context , units , location , repositoryManager ,
126174 children );
127175 if (bestLocation != null ) {
128176 return bestLocation ;
@@ -150,8 +198,8 @@ private IMetadataRepository getMetadataRepository(Element iuLocation, UpdateTarg
150198 log .debug ("No repository found for location " + repoURI + ": " + e );
151199 }
152200 }
153- IMetadataRepository bestLocation = findBestLocation (context , units , location , repositoryManager ,
154- repositories );
201+ MetadataRepositoryUpdate bestLocation = findBestLocation (context , units , location ,
202+ repositoryManager , repositories );
155203 if (bestLocation != null ) {
156204 return bestLocation ;
157205 }
@@ -192,7 +240,7 @@ private IMetadataRepository getMetadataRepository(Element iuLocation, UpdateTarg
192240 log .debug ("Check location " + nextURL + " for updates with " + currentDateString + " > "
193241 + nextDateString + "..." );
194242 try {
195- IMetadataRepository bestLocation = findBestLocation (context , units , location ,
243+ MetadataRepositoryUpdate bestLocation = findBestLocation (context , units , location ,
196244 repositoryManager , List .of (new URI (nextURL )));
197245 if (bestLocation != null ) {
198246 return bestLocation ;
@@ -207,11 +255,12 @@ private IMetadataRepository getMetadataRepository(Element iuLocation, UpdateTarg
207255 }
208256 }
209257 //if nothing else is applicable return the original location repository...
210- return repositoryManager .loadRepository (uri , null );
258+ return new MetadataRepositoryUpdate ( null , repositoryManager .loadRepository (uri , null ) );
211259 }
212260
213- private IMetadataRepository findBestLocation (UpdateTargetMojo context , List <IU > units , ResolvedRepository location ,
214- IMetadataRepositoryManager repositoryManager , Collection <URI > children ) throws ProvisionException {
261+ private MetadataRepositoryUpdate findBestLocation (UpdateTargetMojo context , List <IU > units ,
262+ ResolvedRepository location , IMetadataRepositoryManager repositoryManager , Collection <URI > children )
263+ throws ProvisionException {
215264 List <IU > bestUnits = units ;
216265 URI bestURI = null ;
217266 //we now need to find a repository that has all units and they must have the same or higher version
@@ -223,8 +272,7 @@ private IMetadataRepository findBestLocation(UpdateTargetMojo context, List<IU>
223272 }
224273 }
225274 if (bestURI != null ) {
226- location .element ().setAttribute ("location" , bestURI .toString ());
227- return repositoryManager .loadRepository (bestURI , null );
275+ return new MetadataRepositoryUpdate (bestURI , repositoryManager .loadRepository (bestURI , null ));
228276 }
229277 return null ;
230278 }
@@ -249,6 +297,13 @@ private static Collection<URI> getChildren(IMetadataRepository repository) {
249297 return List .of ();
250298 }
251299
300+ private static boolean isCompatibleChange (VersionUpdate update , UpdateTargetMojo context ) {
301+ if (update .isMajorChange () && !context .isAllowMajorUpdates ()) {
302+ return false ;
303+ }
304+ return true ;
305+ }
306+
252307 private static Collection <URI > expandRepository (URI uri , IMetadataRepositoryManager repositoryManager )
253308 throws ProvisionException {
254309 IMetadataRepository repository = repositoryManager .loadRepository (uri , null );
@@ -326,6 +381,10 @@ public String getId() {
326381 return id ();
327382 }
328383
384+ public void setLocation (URI uri ) {
385+ element ().setAttribute ("location" , uri .toString ());
386+ }
387+
329388 }
330389
331390 private static record IU (String id , String version , Element element ) {
@@ -409,4 +468,93 @@ private static Optional<URI> buildVersionString(int[] versions, String[] prefix,
409468 }
410469 }
411470
471+ private static void describeUpdate (VersionUpdate versionUpdate , MarkdownBuilder builder ) {
472+ IInstallableUnit update = versionUpdate .update ();
473+ builder .addListItem (String .format ("Unit %s was updated from %s to %s" , versionUpdate .id (),
474+ versionUpdate .getPreviousVersion (), update .getVersion ()));
475+ IInstallableUnit current = versionUpdate .current ();
476+ if (current != null ) {
477+ Collection <IRequirement > currentRequirements = current .getRequirements ();
478+ for (IRequirement requirement : update .getRequirements ()) {
479+ if (!currentRequirements .contains (requirement ) && requirement .getMin () > 0 ) {
480+ builder .addListItem2 (
481+ String .format ("additionally requires %s compared to the previous version" , requirement ));
482+ }
483+ }
484+ }
485+ }
486+
487+ private static record VersionUpdate (String id , String previousVersion , IInstallableUnit current ,
488+ IInstallableUnit update ) {
489+
490+ public boolean hasVersionChange () {
491+ if (EMPTY_VERSION .equals (previousVersion )) {
492+ return true ;
493+ }
494+ if (current != null ) {
495+ return !current .getVersion ().equals (update .getVersion ());
496+ }
497+ return !update .getVersion ().toString ().equals (previousVersion );
498+ }
499+
500+ public boolean isMajorChange () {
501+ var currentVersion = getOSGiVersion (current );
502+ var updateVersion = getOSGiVersion (update );
503+ if (isVersion (currentVersion ) && isVersion (updateVersion )) {
504+ return updateVersion .getMajor () > currentVersion .getMajor ();
505+ }
506+ return false ;
507+ }
508+
509+ private boolean isVersion (org .osgi .framework .Version version ) {
510+ return version != null && !org .osgi .framework .Version .emptyVersion .equals (version );
511+ }
512+
513+ public String getPreviousVersion () {
514+ if (current != null ) {
515+ if (!current .getVersion ().toString ().equals (update .getVersion ().toString ())) {
516+ return current .getVersion ().toString ();
517+ }
518+ }
519+ return previousVersion ;
520+ }
521+
522+ }
523+
524+ private static org .osgi .framework .Version getOSGiVersion (IInstallableUnit unit ) {
525+ if (unit != null ) {
526+ try {
527+ return org .osgi .framework .Version .parseVersion (unit .getVersion ().toString ());
528+ } catch (RuntimeException e ) {
529+ //can't parse
530+ }
531+ }
532+ return null ;
533+ }
534+
535+ private static record MetadataRepositoryUpdate (URI uri , IMetadataRepository update ) {
536+
537+ public boolean updateLocation (ResolvedRepository element ) {
538+ if (uri != null ) {
539+ try {
540+ if (new URI (element .getLocation ()).equals (uri )) {
541+ return false ;
542+ }
543+ } catch (URISyntaxException e ) {
544+ }
545+ element .setLocation (uri );
546+ return true ;
547+ }
548+ return false ;
549+ }
550+
551+ public boolean hasUpdate (URI other ) {
552+ if (uri == null ) {
553+ return false ;
554+ }
555+ return !uri .equals (other );
556+ }
557+
558+ }
559+
412560}
0 commit comments