blob: 61c0a2729658172881499a9e263477b2f036a935 [file] [log] [blame]
Stuart McCullochd00f9712009-07-13 10:06:47 +00001package aQute.bnd.build;
2
3import java.io.*;
4import java.util.*;
5import java.util.jar.*;
6
7import aQute.bnd.help.*;
8import aQute.bnd.service.*;
9import aQute.bnd.service.action.*;
10import aQute.bnd.test.*;
11import aQute.lib.osgi.*;
12import aQute.lib.osgi.eclipse.*;
13import aQute.libg.sed.*;
14import aQute.service.scripting.*;
15
16/**
17 * This class is NOT threadsafe
18 *
19 * @author aqute
20 *
21 */
22public class Project extends Processor {
23
24 final static String DEFAULT_ACTIONS = "build; label='Build', test; label='Test', clean; label='Clean', release; label='Release', refreshAll; label=Refresh";
25 public final static String BNDFILE = "bnd.bnd";
26 public final static String BNDCNF = "cnf";
27 final Workspace workspace;
28 long time;
29 int count;
30 boolean preparedPaths;
31 final Collection<Project> dependson = new LinkedHashSet<Project>();
32 final Collection<Container> buildpath = new LinkedHashSet<Container>();
33 final Collection<Container> runpath = new LinkedHashSet<Container>();
34 final Collection<File> sourcepath = new LinkedHashSet<File>();
35 final Collection<File> allsourcepath = new LinkedHashSet<File>();
36 final Collection<Container> bootclasspath = new LinkedHashSet<Container>();
37 final Collection<Container> runbundles = new LinkedHashSet<Container>();
38 File output;
39 File target;
40 boolean inPrepare;
41
42 public Project(Workspace workspace, File projectDir, File buildFile)
43 throws Exception {
44 super(workspace);
45 this.workspace = workspace;
46 setFileMustExist(false);
47 setProperties(buildFile);
48 assert workspace != null;
49 // For backward compatibility reasons, we also read
50 readBuildProperties();
51 }
52
53 public Project(Workspace workspace, File buildDir) throws Exception {
54 this(workspace, buildDir, new File(buildDir, BNDFILE));
55 }
56
57 private void readBuildProperties() throws Exception {
58 try {
59 File f = getFile("build.properties");
60 if (f.isFile()) {
61 Properties p = loadProperties(f);
62 for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) {
63 String key = (String) e.nextElement();
64 String newkey = key;
65 if (key.indexOf('$') >= 0) {
66 newkey = getReplacer().process(key);
67 }
68 setProperty(newkey, p.getProperty(key));
69 }
70 }
71 } catch (Exception e) {
72 e.printStackTrace();
73 }
74 }
75
76 public static Project getUnparented(File propertiesFile) throws Exception {
77 propertiesFile = propertiesFile.getAbsoluteFile();
78 Workspace workspace = new Workspace(propertiesFile.getParentFile());
79 Project project = new Project(workspace, propertiesFile.getParentFile());
80 project.setProperties(propertiesFile);
81 project.setFileMustExist(true);
82 return project;
83 }
84
85 public synchronized boolean isValid() {
86 return getBase().isDirectory() && getPropertiesFile().isFile();
87 }
88
89 /**
90 * Return a new builder that is nicely setup for this project. Please close
91 * this builder after use.
92 *
93 * @param parent
94 * The project builder to use as parent, use this project if null
95 * @return
96 * @throws Exception
97 */
98 public synchronized ProjectBuilder getBuilder(ProjectBuilder parent)
99 throws Exception {
100
101 ProjectBuilder builder;
102
103 if (parent == null)
104 builder = new ProjectBuilder(this);
105 else
106 builder = new ProjectBuilder(parent);
107
108 builder.setBase(getBase());
109
110 for (Container file : getBuildpath()) {
111 builder.addClasspath(file.getFile());
112 }
113
114 for (Container file : getBootclasspath()) {
115 builder.addClasspath(file.getFile());
116 }
117
118 for (File file : getAllsourcepath()) {
119 builder.addSourcepath(file);
120 }
121 return builder;
122 }
123
124 public synchronized int getChanged() {
125 return count;
126 }
127
128 public synchronized void setChanged() {
129 // if (refresh()) {
130 preparedPaths = false;
131 count++;
132 // }
133 }
134
135 public Workspace getWorkspace() {
136 return workspace;
137 }
138
139 public String toString() {
140 return getBase().getName();
141 }
142
143 /**
144 * Set up all the paths
145 */
146
147 public synchronized void prepare() throws Exception {
148 if (inPrepare)
149 throw new CircularDependencyException(toString());
150 if (!preparedPaths) {
151 inPrepare = true;
152 try {
153 dependson.clear();
154 buildpath.clear();
155 sourcepath.clear();
156 allsourcepath.clear();
157 bootclasspath.clear();
158 runpath.clear();
159 runbundles.clear();
160
161 // We use a builder to construct all the properties for
162 // use.
163 setProperty("basedir", getBase().getAbsolutePath());
164
165 // If a bnd.bnd file exists, we read it.
166 // Otherwise, we just do the build properties.
167 if (!getPropertiesFile().isFile()
168 && new File(getBase(), ".classpath").isFile()) {
169 // Get our Eclipse info, we might depend on other projects
170 // though ideally this should become empty and void
171 doEclipseClasspath();
172 }
173
174 // Calculate our source directory
175
176 File src = new File(getBase(), getProperty("src", "src"));
177 if (src.isDirectory()) {
178 sourcepath.add(src);
179 allsourcepath.add(src);
180 } else
181 sourcepath.add(getBase());
182
183 // Set default bin directory
184 output = getFile(getProperty("bin", "bin")).getAbsoluteFile();
185 if (output.isDirectory()) {
186 if (!buildpath.contains(output))
187 buildpath.add(new Container(this, output));
188 } else {
189 if (!output.exists())
190 output.mkdirs();
191 if (!output.isDirectory())
192 error("Can not find output directory: " + output);
193 }
194
195 // Where we store all our generated stuff.
196 target = getFile(getProperty("target", "generated"));
197
198 // We might have some other projects we want build
199 // before we do anything, but these projects are not in
200 // our path. The -dependson allows you to build them before.
201
202 List<Project> dependencies = new ArrayList<Project>();
203
204 String dp = getProperty(Constants.DEPENDSON);
205 Set<String> requiredProjectNames = parseHeader(dp).keySet();
206 for (String p : requiredProjectNames) {
207 Project required = getWorkspace().getProject(p);
208 if (required == null)
209 error("No such project " + required + " on "
210 + Constants.DEPENDSON);
211 else {
212 dependencies.add(required);
213 }
214
215 }
216
217 // We have two paths that consists of repo files, projects,
218 // or some other stuff. The doPath routine adds them to the
219 // path and extracts the projects so we can build them before.
220
221 doPath(buildpath, dependencies, parseBuildpath(), bootclasspath);
222 doPath(runpath, dependencies, parseTestpath(), bootclasspath);
223 doPath(runbundles, dependencies, parseTestbundles(), null);
224
225 // We now know all dependend projects. But we also depend
226 // on whatever those projects depend on. This creates an
227 // ordered list without any duplicates. This of course assumes
228 // that there is no circularity. However, this is checked
229 // by the inPrepare flag, will throw an exception if we
230 // are circular.
231
232 Set<Project> done = new HashSet<Project>();
233 done.add(this);
234 allsourcepath.addAll(sourcepath);
235
236 for (Project project : dependencies)
237 project.traverse(dependson, done);
238
239 for (Project project : dependson) {
240 allsourcepath.addAll(project.getSourcepath());
241 }
242 if (isOk())
243 preparedPaths = true;
244 } finally {
245 inPrepare = false;
246 }
247 }
248 }
249
250 private void traverse(Collection<Project> dependencies, Set<Project> visited)
251 throws Exception {
252 if (visited.contains(this))
253 return;
254
255 visited.add(this);
256
257 for (Project project : getDependson())
258 project.traverse(dependencies, visited);
259
260 dependencies.add(this);
261 }
262
263 /**
264 * Iterate over the entries and place the projects on the projects list and
265 * all the files of the entries on the resultpath.
266 *
267 * @param resultpath
268 * The list that gets all the files
269 * @param projects
270 * The list that gets any projects that are entries
271 * @param entries
272 * The input list of classpath entries
273 */
274 private void doPath(Collection<Container> resultpath,
275 Collection<Project> projects, Collection<Container> entries,
276 Collection<Container> bootclasspath) {
277 for (Container cpe : entries) {
278 if (cpe.getError() != null)
279 error(cpe.getError());
280 else {
281 if (cpe.getType() == Container.TYPE.PROJECT) {
282 projects.add(cpe.getProject());
283 }
284 if (bootclasspath != null
285 && cpe.getBundleSymbolicName().startsWith("ee.")
286 || cpe.getAttributes().containsKey("boot"))
287 bootclasspath.add(cpe);
288 else
289 resultpath.add(cpe);
290 }
291 }
292 }
293
294 /**
295 * Parse the list of bundles that are a prerequisite to this project.
296 *
297 * Bundles are listed in repo specific names. So we just let our repo
298 * plugins iterate over the list of bundles and we get the highest version
299 * from them.
300 *
301 * @return
302 */
303
304 private List<Container> parseBuildpath() throws Exception {
305 return getBundles(Constants.STRATEGY_LOWEST,
306 getProperty(Constants.BUILDPATH));
307 }
308
309 private List<Container> parseTestpath() throws Exception {
310 return getBundles(Constants.STRATEGY_HIGHEST,
311 getProperty(Constants.RUNPATH));
312 }
313
314 private List<Container> parseTestbundles() throws Exception {
315 return getBundles(Constants.STRATEGY_HIGHEST,
316 getProperty(Constants.RUNBUNDLES));
317 }
318
319 /**
320 * Analyze the header and return a list of files that should be on the
321 * build, test or some other path. The list is assumed to be a list of bsns
322 * with a version specification. The special case of version=project
323 * indicates there is a project in the same workspace. The path to the
324 * output directory is calculated. The default directory ${bin} can be
325 * overridden with the output attribute.
326 *
327 * @param strategy
328 * STRATEGY_LOWEST or STRATEGY_HIGHEST
329 * @param spec
330 * The header
331 * @return
332 */
333 public List<Container> getBundles(int strategy, String spec)
334 throws Exception {
335 List<Container> result = new ArrayList<Container>();
336 Map<String, Map<String, String>> bundles = parseHeader(spec);
337
338 try {
339 for (Iterator<Map.Entry<String, Map<String, String>>> i = bundles
340 .entrySet().iterator(); i.hasNext();) {
341 Map.Entry<String, Map<String, String>> entry = i.next();
342 String bsn = entry.getKey();
343 Map<String, String> attrs = entry.getValue();
344
345 Container found = null;
346
347 String versionRange = attrs.get("version");
348
349 if (versionRange != null && versionRange.equals("project")) {
350 Project project = getWorkspace().getProject(bsn);
351 if (project.exists()) {
352 File f = project.getOutput();
353 found = new Container(project, bsn, "project",
354 Container.TYPE.PROJECT, f, null, attrs);
355 } else {
356 error(
357 "Reference to project that does not exist in workspace\n"
358 + " Project %s\n"
359 + " Specification %s", bsn, spec);
360 continue;
361 }
362 } else if (versionRange != null && versionRange.equals("file")) {
363 File f = getFile(bsn);
364 String error = null;
365 if (!f.exists())
366 error = "File does not exist";
367 if (f.getName().endsWith(".lib")) {
368 found = new Container(this, bsn, "file",
369 Container.TYPE.LIBRARY, f, error, attrs);
370 } else {
371 found = new Container(this, bsn, "file",
372 Container.TYPE.EXTERNAL, f, error, attrs);
373 }
374 } else {
375 found = getBundle(bsn, versionRange, strategy, attrs);
376 }
377
378 if (found != null) {
379 List<Container> libs = found.getMembers();
380 for (Container cc : libs) {
381 if (result.contains(cc))
382 warning("Multiple bundles with the same final URL: "
383 + cc);
384
385 result.add(cc);
386 }
387 } else {
388 // Oops, not a bundle in sight :-(
389 Container x = new Container(this, bsn, versionRange,
390 Container.TYPE.ERROR, null, bsn + ";version="
391 + versionRange + " not found", attrs);
392 result.add(x);
393 warning("Can not find URL for bsn " + bsn);
394 }
395 }
396 } catch (Exception e) {
397 error("While tring to get the bundles from " + spec, e);
398 e.printStackTrace();
399 }
400 return result;
401 }
402
403 public long getTime() {
404 return time;
405 }
406
407 public Collection<Project> getDependson() throws Exception {
408 prepare();
409 return dependson;
410 }
411
412 public Collection<Container> getBuildpath() throws Exception {
413 prepare();
414 return buildpath;
415 }
416
417 public Collection<Container> getRunpath() throws Exception {
418 prepare();
419 return runpath;
420 }
421
422 public Collection<Container> getRunbundles() throws Exception {
423 prepare();
424 return runbundles;
425 }
426
427 public Collection<File> getSourcepath() throws Exception {
428 prepare();
429 return sourcepath;
430 }
431
432 public Collection<File> getAllsourcepath() throws Exception {
433 prepare();
434 return allsourcepath;
435 }
436
437 public Collection<Container> getBootclasspath() throws Exception {
438 prepare();
439 return bootclasspath;
440 }
441
442 public File getOutput() throws Exception {
443 prepare();
444 return output;
445 }
446
447 private void doEclipseClasspath() throws Exception {
448 EclipseClasspath eclipse = new EclipseClasspath(this, getWorkspace()
449 .getBase(), getBase());
450 eclipse.setRecurse(false);
451
452 // We get the file directories but in this case we need
453 // to tell ant that the project names
454 for (File dependent : eclipse.getDependents()) {
455 Project required = workspace.getProject(dependent.getName());
456 dependson.add(required);
457 }
458 for (File f : eclipse.getClasspath()) {
459 buildpath.add(new Container(f));
460 }
461 for (File f : eclipse.getBootclasspath()) {
462 bootclasspath.add(new Container(f));
463 }
464 sourcepath.addAll(eclipse.getSourcepath());
465 allsourcepath.addAll(eclipse.getAllSources());
466 output = eclipse.getOutput();
467 }
468
469 public String _p_dependson(String args[]) throws Exception {
470 return list(args, toFiles(getDependson()));
471 }
472
473 private Collection<?> toFiles(Collection<Project> projects) {
474 List<File> files = new ArrayList<File>();
475 for (Project p : projects) {
476 files.add(p.getBase());
477 }
478 return files;
479 }
480
481 public String _p_buildpath(String args[]) throws Exception {
482 return list(args, getBuildpath());
483 }
484
485 public String _p_testpath(String args[]) throws Exception {
486 return list(args, getRunpath());
487 }
488
489 public String _p_sourcepath(String args[]) throws Exception {
490 return list(args, getSourcepath());
491 }
492
493 public String _p_allsourcepath(String args[]) throws Exception {
494 return list(args, getAllsourcepath());
495 }
496
497 public String _p_bootclasspath(String args[]) throws Exception {
498 return list(args, getBootclasspath());
499 }
500
501 public String _p_output(String args[]) throws Exception {
502 if (args.length != 1)
503 throw new IllegalArgumentException(
504 "${output} should not have arguments");
505 return getOutput().getAbsolutePath();
506 }
507
508 private String list(String[] args, Collection<?> list) {
509 if (args.length > 3)
510 throw new IllegalArgumentException(
511 "${"
512 + args[0]
513 + "[;<separator>]} can only take a separator as argument, has "
514 + Arrays.toString(args));
515
516 String separator = ",";
517 if (args.length == 2) {
518 separator = args[1];
519 }
520
521 return join(list, separator);
522 }
523
524 protected Object[] getMacroDomains() {
525 return new Object[] { workspace };
526 }
527
528 public File release(Jar jar) throws Exception {
529 String name = getProperty(Constants.RELEASEREPO);
530 return release(name, jar);
531 }
532
533 /**
534 * Release
535 * @param name The repository name
536 * @param jar
537 * @return
538 * @throws Exception
539 */
540 public File release(String name, Jar jar) throws Exception {
541 List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
542 RepositoryPlugin rp = null;
543 for (RepositoryPlugin plugin : plugins) {
544 if (!plugin.canWrite()) {
545 continue;
546 }
547 if (name == null) {
548 rp = plugin;
549 break;
550 } else if (name.equals(plugin.getName())){
551 rp = plugin;
552 break;
553 }
554 }
555
556 if (rp != null) {
557 try {
558 return rp.put(jar);
559 } catch (Exception e) {
560 error("Deploying " + jar.getName() + " on " + rp.getName(), e);
561 } finally {
562 jar.close();
563 }
564 }
565 return null;
566
567 }
568
569 public void release(boolean test) throws Exception {
570 String name = getProperty(Constants.RELEASEREPO);
571 release(name, test);
572 }
573
574 /**
575 * Release
576 * @param name The respository name
577 * @param test Run testcases
578 * @throws Exception
579 */
580 public void release(String name, boolean test) throws Exception {
581 File[] jars = build(test);
582 // If build fails jars will be null
583 if (jars == null) {
584 return;
585 }
586 for (File jar : jars) {
587 Jar j = new Jar(jar);
588 release(name, j);
589 j.close();
590 }
591
592 }
593
594 /**
595 * Get a bundle from one of the plugin repositories.
596 *
597 * @param bsn
598 * The bundle symbolic name
599 * @param range
600 * The version range
601 * @param lowest
602 * set to LOWEST or HIGHEST
603 * @return the file object that points to the bundle or null if not found
604 * @throws Exception
605 * when something goes wrong
606 */
607 public Container getBundle(String bsn, String range, int strategy,
608 Map<String, String> attrs) throws Exception {
609 List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
610
611 // If someone really wants the latest, lets give it to them.
612 // regardless of they asked for a lowest strategy
613 if (range != null && range.equals("latest"))
614 strategy = STRATEGY_HIGHEST;
615
616 for (RepositoryPlugin plugin : plugins) {
617 File[] results = plugin.get(bsn, range);
618 if (results != null && results.length > 0) {
619 File f = results[strategy == STRATEGY_LOWEST ? 0
620 : results.length - 1];
621
622 if (f.getName().endsWith("lib"))
623 return new Container(this, bsn, range,
624 Container.TYPE.LIBRARY, f, null, attrs);
625 else
626 return new Container(this, bsn, range, Container.TYPE.REPO,
627 f, null, attrs);
628 }
629 }
630
631 return new Container(this, bsn, range, Container.TYPE.ERROR, null, bsn
632 + ";version=" + range + " Not found in " + plugins, null);
633 }
634
635 /**
636 * Deploy the file (which must be a bundle) into the repository.
637 *
638 * @param name The repository name
639 * @param file
640 * bundle
641 */
642 public void deploy(String name, File file) throws Exception {
643 List<RepositoryPlugin> plugins = getPlugins(RepositoryPlugin.class);
644 RepositoryPlugin rp = null;
645 for (RepositoryPlugin plugin : plugins) {
646 if (!plugin.canWrite()) {
647 continue;
648 }
649 if (name == null) {
650 rp = plugin;
651 break;
652 } else if (name.equals(plugin.getName())){
653 rp = plugin;
654 break;
655 }
656 }
657
658 if (rp != null) {
659 Jar jar = new Jar(file);
660 try {
661 rp.put(jar);
662 return;
663 } catch (Exception e) {
664 error("Deploying " + file + " on " + rp.getName(), e);
665 } finally {
666 jar.close();
667 }
668 return;
669 }
670 trace("No repo found " + file);
671 throw new IllegalArgumentException("No repository found for " + file);
672 }
673
674 /**
675 * Deploy the file (which must be a bundle) into the repository.
676 *
677 * @param file
678 * bundle
679 */
680 public void deploy(File file) throws Exception {
681 String name = getProperty(Constants.DEPLOYREPO);
682 deploy(name, file);
683 }
684 /**
685 * Macro access to the repository
686 *
687 * ${repo;<bsn>[;<version>[;<low|high>]]}
688 */
689
690 public String _repo(String args[]) throws Exception {
691 if (args.length < 2)
692 throw new IllegalArgumentException(
693 "Too few arguments for repo, syntax=: ${repo ';'<bsn> [ ; <version> ]}");
694
695 String bsns = args[1];
696 String version = null;
697 int strategy = Constants.STRATEGY_HIGHEST;
698
699 if (args.length > 2) {
700 version = args[2];
701 if (args.length == 4) {
702 if (args[3].equalsIgnoreCase("HIGHEST"))
703 strategy = Constants.STRATEGY_HIGHEST;
704 else if (args[3].equalsIgnoreCase("LOWEST"))
705 strategy = STRATEGY_LOWEST;
706 else
707 error("${repo;<bsn>;<version>;<'highest'|'lowest'>} macro requires a strategy of 'highest' or 'lowest', and is "
708 + args[3]);
709 }
710 }
711
712 Collection<String> parts = split(bsns);
713 List<String> paths = new ArrayList<String>();
714
715 for (String bsn : parts) {
716 Container jar = getBundle(bsn, version, strategy, null);
717 if (jar.getError() == null) {
718 paths.add(jar.getFile().getAbsolutePath());
719 } else {
720 error("The ${repo} macro could not find " + bsn
721 + " in the repo, because " + jar.getError() + "\n"
722 + "Repositories : "
723 + getPlugins(RepositoryPlugin.class) + "\n"
724 + "Strategy : " + strategy + "\n"
725 + "Bsn : " + bsn + ";version=" + version);
726 }
727 }
728 return join(paths);
729 }
730
731 public File getTarget() throws Exception {
732 prepare();
733 return target;
734 }
735
736 public File[] build(boolean underTest) throws Exception {
737 ProjectBuilder builder = getBuilder(null);
738 if (underTest)
739 builder.setProperty(Constants.UNDERTEST, "true");
740 Jar jars[] = builder.builds();
741 File files[] = new File[jars.length];
742
743 File target = getTarget();
744 target.mkdirs();
745
746 for (int i = 0; i < jars.length; i++) {
747 Jar jar = jars[i];
748 try {
749 String bsn = jar.getName();
750 files[i] = new File(target, bsn + ".jar");
751 String msg = "";
752 if (!files[i].exists()
753 || files[i].lastModified() < jar.lastModified()) {
754 reportNewer(files[i].lastModified(), jar);
755 files[i].delete();
756 jar.write(files[i]);
757 } else {
758 msg = "(not modified since "
759 + new Date(files[i].lastModified()) + ")";
760 }
761 trace(jar.getName() + " (" + files[i].getName() + ") "
762 + jar.getResources().size() + " " + msg);
763 } finally {
764 jar.close();
765 }
766 }
767 getInfo(builder);
768 builder.close();
769 if (isOk())
770 return files;
771 else
772 return null;
773 }
774
775 private void reportNewer(long lastModified, Jar jar) {
776 if (isTrue(getProperty(Constants.REPORTNEWER))) {
777 StringBuilder sb = new StringBuilder();
778 String del = "Newer than " + new Date(lastModified);
779 for (Map.Entry<String, Resource> entry : jar.getResources()
780 .entrySet()) {
781 if (entry.getValue().lastModified() > lastModified) {
782 sb.append(del);
783 del = ", \n ";
784 sb.append(entry.getKey());
785 }
786 }
787 if (sb.length() > 0)
788 warning(sb.toString());
789 }
790 }
791
792 /**
793 * Refresh if we are based on stale data. This also implies our workspace.
794 */
795 public boolean refresh() {
796 boolean changed = false;
797 if (isCnf()) {
798 changed = workspace.refresh();
799 }
800 return super.refresh() || changed;
801 }
802
803 public boolean isCnf() {
804 return getBase().getName().equals(Workspace.CNFDIR);
805 }
806
807 public void propertiesChanged() {
808 super.propertiesChanged();
809 preparedPaths = false;
810 }
811
812 public String getName() {
813 return getBase().getName();
814 }
815
816 public Map<String, Action> getActions() {
817 Map<String, Action> all = newMap();
818 Map<String, Action> actions = newMap();
819 fillActions(all);
820 getWorkspace().fillActions(all);
821
822 for (Map.Entry<String, Action> action : all.entrySet()) {
823 String key = getReplacer().process(action.getKey());
824 if (key != null && key.trim().length() != 0)
825 actions.put(key, action.getValue());
826 }
827 return actions;
828 }
829
830 public void fillActions(Map<String, Action> all) {
831 Map<String, Map<String, String>> actions = parseHeader(getProperty(
832 "-actions", DEFAULT_ACTIONS));
833 for (Map.Entry<String, Map<String, String>> entry : actions.entrySet()) {
834 String key = Processor.removeDuplicateMarker(entry.getKey());
835 Action action;
836
837 if (entry.getValue().get("script") != null) {
838 // TODO check for the type
839 action = new ScriptAction(entry.getValue().get("type"), entry
840 .getValue().get("script"));
841 } else {
842 action = new ReflectAction(key);
843 }
844 String label = entry.getValue().get("label");
845 all.put(label, action);
846 }
847 }
848
849 public void release() throws Exception {
850 release(false);
851 }
852
853 /**
854 * Release.
855 * @param name The repository name
856 * @throws Exception
857 */
858 public void release(String name) throws Exception {
859 release(name, false);
860 }
861
862 public void clean() throws Exception {
863 File target = getTarget();
864 if (target.isDirectory() && target.getParentFile() != null) {
865 delete(target);
866 }
867 }
868
869 public File[] build() throws Exception {
870 return build(false);
871 }
872
873 public boolean test() throws Exception {
874 boolean ok = true;
875 String testbundles = getProperty(TESTBUNDLES);
876
877 if (testbundles == null) {
878 File jars[] = build(true);
879 for (File jar : jars)
880 ok &= test(jar);
881
882 } else {
883 List<Container> containers = getBundles(STRATEGY_HIGHEST,
884 testbundles);
885 for (Container container : containers) {
886 if (container.getError() == null) {
887 File jar = container.getFile();
888 ok &= test(jar);
889 } else
890 error(container.getError());
891 }
892 }
893 return ok;
894 }
895
896 public boolean test(File f) throws Exception {
897 ProjectLauncher pl = new ProjectLauncher(this);
898 pl.setReport(getProperty("target") + "/" + f.getName().replace(".jar", ".xml"));
899 int errors = pl.run(f);
900 getInfo(pl);
901 if (errors == 0) {
902 trace("ok");
903 return true;
904 } else {
905 error("Failed: " + normalize(f) + ", " + errors + " test"
906 + (errors > 1 ? "s" : "") + " failures, see "
907 + normalize(pl.getTestreport()));
908 return false;
909 }
910 }
911
912 private void delete(File target) {
913 if (target.getParentFile() == null)
914 throw new IllegalArgumentException("Can not delete root!");
915 if (!target.exists())
916 return;
917
918 if (target.isDirectory()) {
919 File sub[] = target.listFiles();
920 for (File s : sub)
921 delete(s);
922 }
923 target.delete();
924 }
925
926 /**
927 * This methods attempts to turn any jar into a valid jar. If this is a
928 * bundle with manifest, a manifest is added based on defaults. If it is a
929 * bundle, but not r4, we try to add the r4 headers.
930 *
931 * @param name
932 * @param in
933 * @return
934 * @throws Exception
935 */
936 public Jar getValidJar(File f) throws Exception {
937 Jar jar = new Jar(f);
938 Manifest manifest = jar.getManifest();
939 if (manifest == null) {
940 trace("Wrapping with all defaults");
941 Builder b = new Builder(this);
942 b.addClasspath(jar);
943 b.setProperty("Bnd-Message", "Wrapped from " + f.getAbsolutePath()
944 + "because lacked manifest");
945 b.setProperty(Constants.EXPORT_PACKAGE, "*");
946 b.setProperty(Constants.IMPORT_PACKAGE, "*;resolution:=optional");
947 jar = b.build();
948 } else if (manifest.getMainAttributes().getValue(
949 Constants.BUNDLE_MANIFESTVERSION) == null) {
950 trace("Not a release 4 bundle, wrapping with manifest as source");
951 Builder b = new Builder(this);
952 b.addClasspath(jar);
953 b.setProperty(Constants.PRIVATE_PACKAGE, "*");
954 b.mergeManifest(manifest);
955 String imprts = manifest.getMainAttributes().getValue(
956 Constants.IMPORT_PACKAGE);
957 if (imprts == null)
958 imprts = "";
959 else
960 imprts += ",";
961 imprts += "*;resolution=optional";
962
963 b.setProperty(Constants.IMPORT_PACKAGE, imprts);
964 b.setProperty("Bnd-Message", "Wrapped from " + f.getAbsolutePath()
965 + "because had incomplete manifest");
966 jar = b.build();
967 }
968 return jar;
969 }
970
971 public String _project(String args[]) {
972 return getBase().getAbsolutePath();
973 }
974
975 public void bump(String mask) throws IOException {
976 Sed sed = new Sed(getReplacer(), getPropertiesFile());
977 sed
978 .replace(
979 "(Bundle-Version\\s*(:|=)\\s*)(([0-9]+(\\.[0-9]+(\\.[0-9]+)?)?))",
980 "$1${version;" + mask + ";$3}");
981 sed.doIt();
982 refresh();
983 }
984
985 public void bump() throws IOException {
986 bump(getProperty(BUMPPOLICY, "=+0"));
987 }
988
989 public void action(String command) throws Exception {
990 Action a = new ReflectAction(command);
991 a.execute(this, command);
992 }
993
994 public String _findfile(String args[]) {
995 File f = getFile(args[1]);
996 List<String> files = new ArrayList<String>();
997 tree(files, f, "", Instruction.getPattern(args[2]));
998 return join(files);
999 }
1000
1001 void tree(List<String> list, File current, String path, Instruction instr) {
1002 if (path.length() > 0)
1003 path = path + "/";
1004
1005 String subs[] = current.list();
1006 for (String sub : subs) {
1007 File f = new File(current, sub);
1008 if (f.isFile()) {
1009 if (instr.matches(sub) && !instr.isNegated())
1010 list.add(path + sub);
1011 } else
1012 tree(list, f, path + sub, instr);
1013 }
1014 }
1015
1016 public void refreshAll() {
1017 workspace.refresh();
1018 refresh();
1019 }
1020
1021 @SuppressWarnings("unchecked")
1022 public void script(String type, String script) throws Exception {
1023 // TODO check tyiping
1024 List<Scripter> scripters = getPlugins(Scripter.class);
1025 if (scripters.isEmpty()) {
1026 error(
1027 "Can not execute script because there are no scripters registered: %s",
1028 script);
1029 return;
1030 }
1031 Map x = (Map) getProperties();
1032 scripters.get(0)
1033 .eval((Map<String, Object>) x, new StringReader(script));
1034 }
1035
1036 public String _repos(String args[]) throws Exception {
1037 List<RepositoryPlugin> repos = getPlugins(RepositoryPlugin.class);
1038 List<String> names = new ArrayList<String>();
1039 for ( RepositoryPlugin rp : repos )
1040 names.add(rp.getName());
1041 return join(names,", ");
1042 }
1043
1044 public String _help(String args[]) throws Exception {
1045 if ( args.length == 1)
1046 return "Specify the option or header you want information for";
1047
1048 Syntax syntax = Syntax.HELP.get(args[1]);
1049 if (syntax == null )
1050 return "No help for " + args[1];
1051
1052 String what = null;
1053 if ( args.length> 2)
1054 what = args[2];
1055
1056 if ( what == null || what.equals("lead"))
1057 return syntax.getLead();
1058 if ( what == null || what.equals("example"))
1059 return syntax.getExample();
1060 if ( what == null || what.equals("pattern"))
1061 return syntax.getPattern();
1062 if ( what == null || what.equals("values"))
1063 return syntax.getValues();
1064
1065 return "Invalid type specified for help: lead, example, pattern, values";
1066 }
1067}