1 package org
.argeo
.build
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.OutputStream
;
6 import java
.nio
.file
.FileVisitResult
;
7 import java
.nio
.file
.Files
;
8 import java
.nio
.file
.Path
;
9 import java
.nio
.file
.PathMatcher
;
10 import java
.nio
.file
.Paths
;
11 import java
.nio
.file
.SimpleFileVisitor
;
12 import java
.nio
.file
.attribute
.BasicFileAttributes
;
13 import java
.util
.ArrayList
;
14 import java
.util
.Arrays
;
15 import java
.util
.Collections
;
16 import java
.util
.HashMap
;
17 import java
.util
.List
;
19 import java
.util
.Objects
;
20 import java
.util
.Properties
;
21 import java
.util
.StringTokenizer
;
22 import java
.util
.jar
.JarEntry
;
23 import java
.util
.jar
.JarOutputStream
;
24 import java
.util
.jar
.Manifest
;
26 import aQute
.bnd
.osgi
.Analyzer
;
27 import aQute
.bnd
.osgi
.Jar
;
30 private final static String SDK_MK
= "sdk.mk";
32 final Path execDirectory
;
33 final Path sdkSrcBase
;
34 final Path argeoBuildBase
;
35 final Path sdkBuildBase
;
38 public Make() throws IOException
{
39 execDirectory
= Paths
.get(System
.getProperty("user.dir"));
40 Path sdkMkP
= findSdkMk(execDirectory
);
41 Objects
.requireNonNull(sdkMkP
, "No " + SDK_MK
+ " found under " + execDirectory
);
43 Map
<String
, String
> context
= new HashMap
<>();
44 List
<String
> sdkMkLines
= Files
.readAllLines(sdkMkP
);
45 lines
: for (String line
: sdkMkLines
) {
46 StringTokenizer st
= new StringTokenizer(line
, " :=");
47 if (!st
.hasMoreTokens())
49 String key
= st
.nextToken();
50 if (!st
.hasMoreTokens())
52 String value
= st
.nextToken();
53 context
.put(key
, value
);
56 sdkSrcBase
= Paths
.get(context
.computeIfAbsent("SDK_SRC_BASE", (key
) -> {
57 throw new IllegalStateException(key
+ " not found");
59 argeoBuildBase
= sdkSrcBase
.resolve("sdk/argeo-build");
61 sdkBuildBase
= Paths
.get(context
.computeIfAbsent("SDK_BUILD_BASE", (key
) -> {
62 throw new IllegalStateException(key
+ " not found");
64 buildBase
= sdkBuildBase
.resolve(sdkSrcBase
.getFileName());
71 void bundle(Map
<String
, List
<String
>> options
) throws IOException
{
73 subDirs
: for (Path subDir
: Files
.newDirectoryStream(buildBase
, (p
) -> Files
.isDirectory(p
))) {
74 String bundleSymbolicName
= subDir
.getFileName().toString();
75 if (!bundleSymbolicName
.contains("."))
77 generateManifest(bundleSymbolicName
, subDir
);
81 subDirs
: for (Path subDir
: Files
.newDirectoryStream(buildBase
, (p
) -> Files
.isDirectory(p
))) {
82 String bundleSymbolicName
= subDir
.getFileName().toString();
83 if (!bundleSymbolicName
.contains("."))
85 Path source
= sdkSrcBase
.resolve(subDir
.getFileName());
86 if (!Files
.exists(source
))
88 Path jarP
= buildBase
.resolve(subDir
.getFileName() + ".jar");
89 createJar(source
, subDir
, jarP
);
98 void generateManifest(String bundleSymbolicName
, Path compiled
) throws IOException
{
99 Properties properties
= new Properties();
100 Path argeoBnd
= argeoBuildBase
.resolve("argeo.bnd");
101 try (InputStream in
= Files
.newInputStream(argeoBnd
)) {
104 // FIXME make it configurable
105 Path branchBnd
= sdkSrcBase
.resolve("cnf/unstable.bnd");
106 try (InputStream in
= Files
.newInputStream(branchBnd
)) {
110 Path bndBnd
= compiled
.resolve("bnd.bnd");
111 try (InputStream in
= Files
.newInputStream(bndBnd
)) {
116 properties
.put("Bundle-SymbolicName", bundleSymbolicName
);
118 // Calculate MANIFEST
119 Path binP
= compiled
.resolve("bin");
121 try (Analyzer bndAnalyzer
= new Analyzer()) {
122 bndAnalyzer
.setProperties(properties
);
123 Jar jar
= new Jar(bundleSymbolicName
, binP
.toFile());
124 bndAnalyzer
.setJar(jar
);
125 manifest
= bndAnalyzer
.calcManifest();
127 // keys: for (Object key : manifest.getMainAttributes().keySet()) {
128 // System.out.println(key + ": " + manifest.getMainAttributes().getValue(key.toString()));
130 } catch (Exception e
) {
131 throw new RuntimeException("Bnd analysis of " + compiled
+ " failed", e
);
135 Path manifestP
= compiled
.resolve("META-INF/MANIFEST.MF");
136 Files
.createDirectories(manifestP
.getParent());
137 try (OutputStream out
= Files
.newOutputStream(manifestP
)) {
145 void createJar(Path source
, Path compiled
, Path jarP
) throws IOException
{
147 Path manifestP
= compiled
.resolve("META-INF/MANIFEST.MF");
148 if (!Files
.exists(manifestP
))
149 throw new IllegalStateException("Manifest " + manifestP
+ " not found");
151 try (InputStream in
= Files
.newInputStream(manifestP
)) {
152 manifest
= new Manifest(in
);
153 } catch (IOException e
) {
154 throw new IllegalStateException("Cannot read manifest " + manifestP
, e
);
158 List
<PathMatcher
> excludes
= new ArrayList
<>();
159 Path excludesP
= argeoBuildBase
.resolve("excludes.txt");
160 for (String line
: Files
.readAllLines(excludesP
)) {
161 PathMatcher pathMatcher
= excludesP
.getFileSystem().getPathMatcher("glob:" + line
);
162 excludes
.add(pathMatcher
);
165 Files
.createDirectories(jarP
.getParent());
166 try (JarOutputStream jarOut
= new JarOutputStream(Files
.newOutputStream(jarP
), manifest
)) {
167 // add all classes first
168 Path binP
= compiled
.resolve("bin");
169 Files
.walkFileTree(binP
, new SimpleFileVisitor
<Path
>() {
172 public FileVisitResult
visitFile(Path file
, BasicFileAttributes attrs
) throws IOException
{
173 jarOut
.putNextEntry(new JarEntry(binP
.relativize(file
).toString()));
174 Files
.copy(file
, jarOut
);
175 return FileVisitResult
.CONTINUE
;
180 Files
.walkFileTree(source
, new SimpleFileVisitor
<Path
>() {
183 public FileVisitResult
preVisitDirectory(Path dir
, BasicFileAttributes attrs
) throws IOException
{
184 Path relativeP
= source
.relativize(dir
);
185 for (PathMatcher exclude
: excludes
)
186 if (exclude
.matches(relativeP
))
187 return FileVisitResult
.SKIP_SUBTREE
;
189 return FileVisitResult
.CONTINUE
;
193 public FileVisitResult
visitFile(Path file
, BasicFileAttributes attrs
) throws IOException
{
194 Path relativeP
= source
.relativize(file
);
195 for (PathMatcher exclude
: excludes
)
196 if (exclude
.matches(relativeP
))
197 return FileVisitResult
.CONTINUE
;
198 JarEntry entry
= new JarEntry(relativeP
.toString());
199 jarOut
.putNextEntry(entry
);
200 Files
.copy(file
, jarOut
);
201 return FileVisitResult
.CONTINUE
;
207 // TODO add effective BND, Eclipse project file, etc., in order to be able to
209 Path srcP
= source
.resolve("src");
210 Files
.walkFileTree(srcP
, new SimpleFileVisitor
<Path
>() {
213 public FileVisitResult
visitFile(Path file
, BasicFileAttributes attrs
) throws IOException
{
214 jarOut
.putNextEntry(new JarEntry("OSGI-OPT/src/" + srcP
.relativize(file
).toString()));
215 Files
.copy(file
, jarOut
);
216 return FileVisitResult
.CONTINUE
;
224 private Path
findSdkMk(Path directory
) {
225 Path sdkMkP
= directory
.resolve(SDK_MK
);
226 if (Files
.exists(sdkMkP
)) {
227 return sdkMkP
.toAbsolutePath();
229 if (directory
.getParent() == null)
231 return findSdkMk(directory
.getParent());
235 public static void main(String
... args
) throws IOException
{
236 if (args
.length
== 0)
237 throw new IllegalArgumentException("At least an action must be provided");
239 String action
= args
[actionIndex
];
240 if (args
.length
> actionIndex
+ 1 && args
[actionIndex
+ 1].startsWith("-"))
241 throw new IllegalArgumentException(
242 "Action " + action
+ " must be followed by an option: " + Arrays
.asList(args
));
244 Map
<String
, List
<String
>> options
= new HashMap
<>();
245 String currentOption
= null;
246 List
<String
> currentOptionValues
= null;
247 for (int i
= actionIndex
+ 1; i
< args
.length
; i
++) {
248 if (args
[i
].startsWith("-")) {
249 if (currentOption
!= null) {
250 assert currentOptionValues
!= null;
251 if (!options
.containsKey(currentOption
))
252 options
.put(currentOption
, new ArrayList
<>());
253 options
.get(currentOption
).addAll(currentOptionValues
);
255 currentOption
= args
[i
];
256 currentOptionValues
= new ArrayList
<>();
258 currentOptionValues
.add(args
[i
]);
261 Make argeoBuild
= new Make();
263 case "bundle" -> argeoBuild
.bundle(options
);
264 default -> throw new IllegalArgumentException("Unkown action: " + action
);