14
14
package org .eclipse .pde .internal .core .bnd ;
15
15
16
16
import java .io .File ;
17
+ import java .io .IOException ;
18
+ import java .io .InputStream ;
19
+ import java .util .ArrayList ;
17
20
import java .util .List ;
18
21
import java .util .Map ;
19
22
import java .util .Objects ;
28
31
import org .eclipse .core .resources .IResourceDelta ;
29
32
import org .eclipse .core .resources .IResourceDeltaVisitor ;
30
33
import org .eclipse .core .resources .IncrementalProjectBuilder ;
34
+ import org .eclipse .core .resources .ResourcesPlugin ;
31
35
import org .eclipse .core .runtime .CoreException ;
32
36
import org .eclipse .core .runtime .ICoreRunnable ;
33
37
import org .eclipse .core .runtime .IProgressMonitor ;
34
38
import org .eclipse .core .runtime .jobs .IJobChangeEvent ;
35
39
import org .eclipse .core .runtime .jobs .Job ;
36
40
import org .eclipse .core .runtime .jobs .JobChangeAdapter ;
41
+ import org .eclipse .osgi .service .resolver .BundleDescription ;
42
+ import org .eclipse .osgi .service .resolver .BundleSpecification ;
43
+ import org .eclipse .osgi .service .resolver .ExportPackageDescription ;
44
+ import org .eclipse .osgi .service .resolver .ImportPackageSpecification ;
45
+ import org .eclipse .osgi .service .resolver .StateObjectFactory ;
46
+ import org .eclipse .osgi .util .ManifestElement ;
37
47
import org .eclipse .osgi .util .NLS ;
48
+ import org .eclipse .pde .core .plugin .IPluginModelBase ;
38
49
import org .eclipse .pde .internal .core .ICoreConstants ;
39
50
import org .eclipse .pde .internal .core .PDECore ;
40
51
import org .eclipse .pde .internal .core .PDECoreMessages ;
52
+ import org .eclipse .pde .internal .core .PluginModelManager ;
41
53
import org .eclipse .pde .internal .core .builders .PDEMarkerFactory ;
42
54
import org .eclipse .pde .internal .core .natures .BndProject ;
43
55
import org .eclipse .pde .internal .core .project .PDEProject ;
56
+ import org .osgi .framework .BundleException ;
57
+ import org .osgi .framework .FrameworkUtil ;
44
58
45
59
import aQute .bnd .build .Project ;
46
60
import aQute .bnd .build .ProjectBuilder ;
@@ -51,10 +65,6 @@ public class BndBuilder extends IncrementalProjectBuilder {
51
65
52
66
private static final String CLASS_EXTENSION = ".class" ; //$NON-NLS-1$
53
67
54
- // This is currently disabled as it sometimes lead to jar not generated as
55
- // JDT is clearing the outputfolder while the build is running, need to
56
- // investigate if we can avoid this and it actually has benefits to build
57
- // everything async.
58
68
private static final boolean USE_JOB = false ;
59
69
60
70
private static final Predicate <IResource > CLASS_FILTER = resource -> {
@@ -76,6 +86,9 @@ protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor
76
86
Job buildJob = buildJobMap .compute (project , (p , oldJob ) -> {
77
87
Job job = Job .create (NLS .bind (PDECoreMessages .BundleBuilder_building , project .getName ()),
78
88
new BndBuild (p , oldJob ));
89
+ // The job is given a workspace lock to prevent other
90
+ // buildjobs to run concurrently
91
+ job .setRule (ResourcesPlugin .getWorkspace ().getRoot ());
79
92
job .addJobChangeListener (new JobChangeAdapter () {
80
93
@ Override
81
94
public void done (IJobChangeEvent event ) {
@@ -86,7 +99,8 @@ public void done(IJobChangeEvent event) {
86
99
});
87
100
buildJob .schedule ();
88
101
} else {
89
- buildProjectJar (project , monitor );
102
+ List <IProject > affected = buildProjectJar (project , monitor );
103
+ requestProjectsRebuild (affected );
90
104
}
91
105
}
92
106
return new IProject [] { project };
@@ -127,14 +141,14 @@ public void run(IProgressMonitor monitor) {
127
141
128
142
}
129
143
130
- private static void buildProjectJar (IProject project , IProgressMonitor monitor ) {
144
+ private static List < IProject > buildProjectJar (IProject project , IProgressMonitor monitor ) {
131
145
try {
132
146
Optional <Project > bndProject = BndProjectManager .getBndProject (project );
133
147
if (bndProject .isEmpty ()) {
134
- return ;
148
+ return List . of () ;
135
149
}
136
150
if (monitor .isCanceled ()) {
137
- return ;
151
+ return List . of () ;
138
152
}
139
153
try (Project bnd = bndProject .get (); ProjectBuilder builder = new ProjectBuilder (bnd ) {
140
154
@ Override
@@ -160,6 +174,7 @@ public void addClasspath(aQute.bnd.osgi.Jar jar) {
160
174
builder .addBasicPlugin (new MakeJar ());
161
175
builder .setBase (bnd .getBase ());
162
176
ProjectJar jar = new ProjectJar (project , CLASS_FILTER );
177
+ BundleDescription previousBundleDescription = loadBundleDescription (jar .getManifestFile ());
163
178
builder .setJar (jar );
164
179
// build the main jar
165
180
builder .build ();
@@ -188,13 +203,66 @@ public void addClasspath(aQute.bnd.osgi.Jar jar) {
188
203
}
189
204
}
190
205
}
191
- }
192
- if (monitor .isCanceled ()) {
193
- return ;
206
+ jar .getManifestFile ().touch (null );
207
+ BundleDescription bundleDescription = loadBundleDescription (jar .getManifestFile ());
208
+ if (bundleDescription != null ) {
209
+ // TODO compare with previous description and also consider
210
+ // BND project dependencies!
211
+ PluginModelManager modelManager = PDECore .getDefault ().getModelManager ();
212
+ IPluginModelBase [] models = modelManager .getWorkspaceModels ();
213
+ List <IProject > affectedProjects = new ArrayList <>();
214
+ for (IPluginModelBase base : models ) {
215
+ IResource resource = base .getUnderlyingResource ();
216
+ if (resource == null ) {
217
+ continue ;
218
+ }
219
+ IProject modelProject = resource .getProject ();
220
+ if (modelProject .equals (project )) {
221
+ continue ;
222
+ }
223
+ if (isModelAffected (base , bundleDescription )) {
224
+ affectedProjects .add (modelProject );
225
+ }
226
+ if (monitor .isCanceled ()) {
227
+ return List .of ();
228
+ }
229
+ }
230
+ return affectedProjects ;
231
+ }
194
232
}
195
233
} catch (Exception e ) {
196
234
PDECore .log (e );
197
235
}
236
+ return List .of ();
237
+ }
238
+
239
+ private static boolean isModelAffected (IPluginModelBase base , BundleDescription bundleDescription ) {
240
+ BundleDescription description = base .getBundleDescription ();
241
+ ImportPackageSpecification [] importPackages = description .getImportPackages ();
242
+ for (ImportPackageSpecification imp : importPackages ) {
243
+ for (ExportPackageDescription exp : bundleDescription .getExportPackages ()) {
244
+ if (imp .isSatisfiedBy (exp )) {
245
+ return true ;
246
+
247
+ }
248
+ }
249
+ }
250
+ for (BundleSpecification req : description .getRequiredBundles ()) {
251
+ if (req .isSatisfiedBy (bundleDescription )) {
252
+ return true ;
253
+ }
254
+ }
255
+ return false ;
256
+ }
257
+
258
+ private static BundleDescription loadBundleDescription (IFile file ) {
259
+ try (InputStream manifest = file .getContents ()) {
260
+ Map <String , String > bundleManifest = ManifestElement .parseBundleManifest (manifest );
261
+ return StateObjectFactory .defaultFactory .createBundleDescription (null ,
262
+ FrameworkUtil .asDictionary (bundleManifest ), null , System .currentTimeMillis ());
263
+ } catch (IOException | CoreException | BundleException e ) {
264
+ return null ;
265
+ }
198
266
}
199
267
200
268
private static boolean requireBuild (IProject project ) {
0 commit comments