PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/bbb-screenshare/jws/webstart/src/main/java/org/bytedeco/javacpp/tools/Builder.java

http://github.com/bigbluebutton/bigbluebutton
Java | 809 lines | 630 code | 34 blank | 145 comment | 204 complexity | 61e17c353da834236d1c6973db697f0f MD5 | raw file
Possible License(s): LGPL-3.0
  1. /*
  2. * Copyright (C) 2011-2016 Samuel Audet
  3. *
  4. * Licensed either under the Apache License, Version 2.0, or (at your option)
  5. * under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation (subject to the "Classpath" exception),
  7. * either version 2, or any later version (collectively, the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. * http://www.gnu.org/licenses/
  13. * http://www.gnu.org/software/classpath/license.html
  14. *
  15. * or as provided in the LICENSE.txt file that accompanied this code.
  16. * Unless required by applicable law or agreed to in writing, software
  17. * distributed under the License is distributed on an "AS IS" BASIS,
  18. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. * See the License for the specific language governing permissions and
  20. * limitations under the License.
  21. */
  22. package org.bytedeco.javacpp.tools;
  23. import java.io.File;
  24. import java.io.FileInputStream;
  25. import java.io.FileOutputStream;
  26. import java.io.FilenameFilter;
  27. import java.io.IOException;
  28. import java.io.InputStreamReader;
  29. import java.net.URI;
  30. import java.net.URISyntaxException;
  31. import java.net.URL;
  32. import java.util.ArrayList;
  33. import java.util.Arrays;
  34. import java.util.Collection;
  35. import java.util.LinkedHashMap;
  36. import java.util.LinkedHashSet;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Properties;
  40. import java.util.jar.JarOutputStream;
  41. import java.util.zip.ZipEntry;
  42. import org.bytedeco.javacpp.ClassProperties;
  43. import org.bytedeco.javacpp.Loader;
  44. /**
  45. * The Builder is responsible for coordinating efforts between the Parser, the
  46. * Generator, and the native compiler. It contains the main() method, and basically
  47. * takes care of the tasks one would expect from a command line build tool, but
  48. * can also be used programmatically by setting its properties and calling build().
  49. *
  50. * @author Samuel Audet
  51. */
  52. public class Builder {
  53. /**
  54. * Calls {@link Parser#parse(File, String[], Class)} after creating an instance of the Class.
  55. *
  56. * @param classPath an array of paths to try to load header files from
  57. * @param cls The class annotated with {@link org.bytedeco.javacpp.annotation.Properties}
  58. * and implementing {@link InfoMapper}
  59. * @return the target File produced
  60. * @throws IOException on Java target file writing error
  61. * @throws ParserException on C/C++ header file parsing error
  62. */
  63. File parse(String[] classPath, Class cls) throws IOException, ParserException {
  64. return new Parser(logger, properties).parse(outputDirectory, classPath, cls);
  65. }
  66. /**
  67. * Tries to find automatically include paths for {@code jni.h} and {@code jni_md.h},
  68. * as well as the link and library paths for the {@code jvm} library.
  69. *
  70. * @param properties the Properties containing the paths to update
  71. * @param header to request support for exporting callbacks via generated header file
  72. */
  73. void includeJavaPaths(ClassProperties properties, boolean header) {
  74. if (properties.getProperty("platform", "").startsWith("android")) {
  75. // Android includes its own jni.h file and doesn't have a jvm library
  76. return;
  77. }
  78. String platform = Loader.getPlatform();
  79. final String jvmlink = properties.getProperty("platform.link.prefix", "") +
  80. "jvm" + properties.getProperty("platform.link.suffix", "");
  81. final String jvmlib = properties.getProperty("platform.library.prefix", "") +
  82. "jvm" + properties.getProperty("platform.library.suffix", "");
  83. final String[] jnipath = new String[2];
  84. final String[] jvmpath = new String[2];
  85. FilenameFilter filter = new FilenameFilter() {
  86. @Override public boolean accept(File dir, String name) {
  87. if (new File(dir, "jni.h").exists()) {
  88. jnipath[0] = dir.getAbsolutePath();
  89. }
  90. if (new File(dir, "jni_md.h").exists()) {
  91. jnipath[1] = dir.getAbsolutePath();
  92. }
  93. if (new File(dir, jvmlink).exists()) {
  94. jvmpath[0] = dir.getAbsolutePath();
  95. }
  96. if (new File(dir, jvmlib).exists()) {
  97. jvmpath[1] = dir.getAbsolutePath();
  98. }
  99. return new File(dir, name).isDirectory();
  100. }
  101. };
  102. File javaHome;
  103. try {
  104. javaHome = new File(System.getProperty("java.home")).getParentFile().getCanonicalFile();
  105. } catch (IOException | NullPointerException e) {
  106. logger.warn("Could not include header files from java.home:" + e);
  107. return;
  108. }
  109. ArrayList<File> dirs = new ArrayList<File>(Arrays.asList(javaHome.listFiles(filter)));
  110. while (!dirs.isEmpty()) {
  111. File d = dirs.remove(dirs.size() - 1);
  112. String dpath = d.getPath();
  113. File[] files = d.listFiles(filter);
  114. if (dpath == null || files == null) {
  115. continue;
  116. }
  117. for (File f : files) {
  118. try {
  119. f = f.getCanonicalFile();
  120. } catch (IOException e) { }
  121. if (!dpath.startsWith(f.getPath())) {
  122. dirs.add(f);
  123. }
  124. }
  125. }
  126. if (jnipath[0] != null && jnipath[0].equals(jnipath[1])) {
  127. jnipath[1] = null;
  128. } else if (jnipath[0] == null) {
  129. String macpath = "/System/Library/Frameworks/JavaVM.framework/Headers/";
  130. if (new File(macpath).isDirectory()) {
  131. jnipath[0] = macpath;
  132. }
  133. }
  134. if (jvmpath[0] != null && jvmpath[0].equals(jvmpath[1])) {
  135. jvmpath[1] = null;
  136. }
  137. properties.addAll("platform.includepath", jnipath);
  138. if (platform.equals(properties.getProperty("platform", platform))) {
  139. if (header) {
  140. // We only need libjvm for callbacks exported with the header file
  141. properties.get("platform.link").add(0, "jvm");
  142. properties.addAll("platform.linkpath", jvmpath);
  143. }
  144. if (platform.startsWith("macosx")) {
  145. properties.addAll("platform.framework", "JavaVM");
  146. }
  147. }
  148. }
  149. /**
  150. * Launches and waits for the native compiler to produce a native shared library.
  151. *
  152. * @param sourceFilename the C++ source filename
  153. * @param outputFilename the output filename of the shared library
  154. * @param properties the Properties detailing the compiler options to use
  155. * @return the result of {@link Process#waitFor()}
  156. * @throws IOException
  157. * @throws InterruptedException
  158. */
  159. int compile(String sourceFilename, String outputFilename, ClassProperties properties, File workingDirectory)
  160. throws IOException, InterruptedException {
  161. ArrayList<String> command = new ArrayList<String>();
  162. includeJavaPaths(properties, header);
  163. String platform = Loader.getPlatform();
  164. String compilerPath = properties.getProperty("platform.compiler");
  165. command.add(compilerPath);
  166. {
  167. String p = properties.getProperty("platform.sysroot.prefix", "");
  168. for (String s : properties.get("platform.sysroot")) {
  169. if (new File(s).isDirectory()) {
  170. if (p.endsWith(" ")) {
  171. command.add(p.trim()); command.add(s);
  172. } else {
  173. command.add(p + s);
  174. }
  175. }
  176. }
  177. }
  178. {
  179. String p = properties.getProperty("platform.includepath.prefix", "");
  180. for (String s : properties.get("platform.includepath")) {
  181. if (new File(s).isDirectory()) {
  182. if (p.endsWith(" ")) {
  183. command.add(p.trim()); command.add(s);
  184. } else {
  185. command.add(p + s);
  186. }
  187. }
  188. }
  189. }
  190. command.add(sourceFilename);
  191. Collection<String> allOptions = properties.get("platform.compiler.*");
  192. if (allOptions.isEmpty()) {
  193. allOptions.add("default");
  194. }
  195. for (String s : allOptions) {
  196. if (s == null || s.length() == 0) {
  197. continue;
  198. }
  199. String p = "platform.compiler." + s;
  200. String options = properties.getProperty(p);
  201. if (options != null && options.length() > 0) {
  202. command.addAll(Arrays.asList(options.split(" ")));
  203. } else if (!"default".equals(s)) {
  204. logger.warn("Could not get the property named \"" + p + "\"");
  205. }
  206. }
  207. command.addAll(compilerOptions);
  208. String output = properties.getProperty("platform.compiler.output");
  209. for (int i = 1; i < 2 || output != null; i++,
  210. output = properties.getProperty("platform.compiler.output" + i)) {
  211. if (output != null && output.length() > 0) {
  212. command.addAll(Arrays.asList(output.split(" ")));
  213. }
  214. if (output == null || output.length() == 0 || output.endsWith(" ")) {
  215. command.add(outputFilename);
  216. } else {
  217. command.add(command.remove(command.size() - 1) + outputFilename);
  218. }
  219. }
  220. {
  221. String p = properties.getProperty("platform.linkpath.prefix", "");
  222. String p2 = properties.getProperty("platform.linkpath.prefix2");
  223. for (String s : properties.get("platform.linkpath")) {
  224. if (new File(s).isDirectory()) {
  225. if (p.endsWith(" ")) {
  226. command.add(p.trim()); command.add(s);
  227. } else {
  228. command.add(p + s);
  229. }
  230. if (p2 != null) {
  231. if (p2.endsWith(" ")) {
  232. command.add(p2.trim()); command.add(s);
  233. } else {
  234. command.add(p2 + s);
  235. }
  236. }
  237. }
  238. }
  239. }
  240. {
  241. String p = properties.getProperty("platform.link.prefix", "");
  242. String x = properties.getProperty("platform.link.suffix", "");
  243. int i = command.size(); // to inverse order and satisfy typical compilers
  244. for (String s : properties.get("platform.link")) {
  245. String[] libnameversion = s.split("@");
  246. if (libnameversion.length == 3 && libnameversion[1].length() == 0) {
  247. // Only use the version number when the user gave us a double @
  248. s = libnameversion[0] + libnameversion[2];
  249. } else {
  250. s = libnameversion[0];
  251. }
  252. if (p.endsWith(" ") && x.startsWith(" ")) {
  253. command.add(i, p.trim()); command.add(i + 1, s); command.add(i + 2, x.trim());
  254. } else if (p.endsWith(" ")) {
  255. command.add(i, p.trim()); command.add(i + 1, s + x);
  256. } else if (x.startsWith(" ")) {
  257. command.add(i, p + s); command.add(i + 1, x.trim());
  258. } else {
  259. command.add(i, p + s + x);
  260. }
  261. }
  262. }
  263. {
  264. String p = properties.getProperty("platform.frameworkpath.prefix", "");
  265. for (String s : properties.get("platform.frameworkpath")) {
  266. if (new File(s).isDirectory()) {
  267. if (p.endsWith(" ")) {
  268. command.add(p.trim()); command.add(s);
  269. } else {
  270. command.add(p + s);
  271. }
  272. }
  273. }
  274. }
  275. {
  276. String p = properties.getProperty("platform.framework.prefix", "");
  277. String x = properties.getProperty("platform.framework.suffix", "");
  278. for (String s : properties.get("platform.framework")) {
  279. if (p.endsWith(" ") && x.startsWith(" ")) {
  280. command.add(p.trim()); command.add(s); command.add(x.trim());
  281. } else if (p.endsWith(" ")) {
  282. command.add(p.trim()); command.add(s + x);
  283. } else if (x.startsWith(" ")) {
  284. command.add(p + s); command.add(x.trim());
  285. } else {
  286. command.add(p + s + x);
  287. }
  288. }
  289. }
  290. String text = "";
  291. boolean windows = platform.startsWith("windows");
  292. for (String s : command) {
  293. boolean hasSpaces = s.indexOf(" ") > 0;
  294. if (hasSpaces) {
  295. text += windows ? "\"" : "'";
  296. }
  297. text += s;
  298. if (hasSpaces) {
  299. text += windows ? "\"" : "'";
  300. }
  301. text += " ";
  302. }
  303. logger.info(text);
  304. ProcessBuilder pb = new ProcessBuilder(command);
  305. // Use the library output path as the working directory so that all
  306. // build files, including intermediate ones from MSVC, are dumped there
  307. pb.directory(workingDirectory);
  308. if (environmentVariables != null) {
  309. pb.environment().putAll(environmentVariables);
  310. }
  311. return pb.inheritIO().start().waitFor();
  312. }
  313. /**
  314. * Generates a C++ source file for classes, and compiles everything in
  315. * one shared library when {@code compile == true}.
  316. *
  317. * @param classes the Class objects as input to Generator
  318. * @param outputName the output name of the shared library
  319. * @return the actual File generated, either the compiled library or its source
  320. * @throws IOException
  321. * @throws InterruptedException
  322. */
  323. File generateAndCompile(Class[] classes, String outputName) throws IOException, InterruptedException {
  324. File outputFile = null, outputPath = outputDirectory;
  325. ClassProperties p = Loader.loadProperties(classes, properties, true);
  326. String platform = p.getProperty("platform");
  327. String sourcePrefix = new File(outputPath, outputName).getPath();
  328. String sourceSuffix = p.getProperty("platform.source.suffix", ".cpp");
  329. String libraryPath = p.getProperty("platform.library.path", "");
  330. String libraryName = p.getProperty("platform.library.prefix", "") + outputName + p.getProperty("platform.library.suffix", "");
  331. if (outputPath == null) {
  332. URI uri = null;
  333. try {
  334. String resourceName = '/' + classes[classes.length - 1].getName().replace('.', '/') + ".class";
  335. String resourceURL = classes[classes.length - 1].getResource(resourceName).toString();
  336. File packageDir = new File(uri = new URI(resourceURL.substring(0, resourceURL.lastIndexOf('/') + 1)));
  337. File targetDir = libraryPath.length() > 0
  338. ? new File(uri = new URI(resourceURL.substring(0, resourceURL.length() - resourceName.length() + 1)))
  339. : new File(packageDir, platform);
  340. outputPath = new File(targetDir, libraryPath);
  341. sourcePrefix = new File(packageDir, outputName).getPath();
  342. } catch (URISyntaxException e) {
  343. throw new RuntimeException(e);
  344. } catch (IllegalArgumentException e) {
  345. throw new RuntimeException(e + ": " + uri);
  346. }
  347. }
  348. if (!outputPath.exists()) {
  349. outputPath.mkdirs();
  350. }
  351. Generator generator = new Generator(logger, p);
  352. String sourceFilename = sourcePrefix + sourceSuffix;
  353. String headerFilename = header ? sourcePrefix + ".h" : null;
  354. String classPath = System.getProperty("java.class.path");
  355. for (String s : classScanner.getClassLoader().getPaths()) {
  356. classPath += File.pathSeparator + s;
  357. }
  358. logger.info("Generating " + sourceFilename);
  359. if (generator.generate(sourceFilename, headerFilename, classPath, classes)) {
  360. generator.close();
  361. if (compile) {
  362. logger.info("Compiling " + outputPath.getPath() + File.separator + libraryName);
  363. int exitValue = compile(sourceFilename, libraryName, p, outputPath);
  364. if (exitValue == 0) {
  365. if (deleteJniFiles) {
  366. logger.info("Deleting " + sourceFilename);
  367. new File(sourceFilename).delete();
  368. } else {
  369. logger.info("Keeping " + sourceFilename);
  370. }
  371. outputFile = new File(outputPath, libraryName);
  372. } else {
  373. System.exit(exitValue);
  374. }
  375. } else {
  376. outputFile = new File(sourceFilename);
  377. }
  378. } else {
  379. logger.info("Nothing generated for " + sourceFilename);
  380. }
  381. return outputFile;
  382. }
  383. /**
  384. * Stores all the files in the given JAR file. Also attempts to root the paths
  385. * of the filenames to each element of a list of classpaths.
  386. *
  387. * @param jarFile the JAR file to create
  388. * @param classPath an array of paths to try to use as root for classes
  389. * @param files a list of files to store in the JAR file
  390. * @throws IOException
  391. */
  392. void createJar(File jarFile, String[] classPath, File ... files) throws IOException {
  393. logger.info("Creating " + jarFile);
  394. JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile));
  395. for (File f : files) {
  396. String name = f.getPath();
  397. if (classPath != null) {
  398. // Store only the path relative to the classpath so that
  399. // our Loader may use the package name of the associated
  400. // class to get the file as a resource from the ClassLoader.
  401. String[] names = new String[classPath.length];
  402. for (int i = 0; i < classPath.length; i++) {
  403. String path = new File(classPath[i]).getCanonicalPath();
  404. if (name.startsWith(path)) {
  405. names[i] = name.substring(path.length() + 1);
  406. }
  407. }
  408. // Retain only the shortest relative name.
  409. for (int i = 0; i < names.length; i++) {
  410. if (names[i] != null && names[i].length() < name.length()) {
  411. name = names[i];
  412. }
  413. }
  414. }
  415. ZipEntry e = new ZipEntry(name.replace(File.separatorChar, '/'));
  416. e.setTime(f.lastModified());
  417. jos.putNextEntry(e);
  418. FileInputStream fis = new FileInputStream(f);
  419. byte[] buffer = new byte[1024];
  420. int length;
  421. while ((length = fis.read(buffer)) != -1) {
  422. jos.write(buffer, 0, length);
  423. }
  424. fis.close();
  425. jos.closeEntry();
  426. }
  427. jos.close();
  428. }
  429. /**
  430. * Default constructor that simply initializes everything.
  431. */
  432. public Builder() {
  433. this(Logger.create(Builder.class));
  434. }
  435. /**
  436. * Constructor that simply initializes everything.
  437. * @param logger where to send messages
  438. */
  439. public Builder(Logger logger) {
  440. this.logger = logger;
  441. System.setProperty("org.bytedeco.javacpp.loadlibraries", "false");
  442. properties = Loader.loadProperties();
  443. classScanner = new ClassScanner(logger, new ArrayList<Class>(),
  444. new UserClassLoader(Thread.currentThread().getContextClassLoader()));
  445. compilerOptions = new ArrayList<String>();
  446. }
  447. /** Logger where to send debug, info, warning, and error messages. */
  448. final Logger logger;
  449. /** The directory where the generated files and compiled shared libraries get written to.
  450. * By default they are placed in the same directory as the {@code .class} file. */
  451. File outputDirectory = null;
  452. /** The name of the output generated source file or shared library. This enables single-
  453. * file output mode. By default, the top-level enclosing classes get one file each. */
  454. String outputName = null;
  455. /** The name of the JAR file to create, if not {@code null}. */
  456. String jarPrefix = null;
  457. /** If true, compiles the generated source file to a shared library and deletes source. */
  458. boolean compile = true;
  459. /** If true, preserves the generated C++ JNI files after compilation */
  460. boolean deleteJniFiles = true;
  461. /** If true, also generates C++ header files containing declarations of callback functions. */
  462. boolean header = false;
  463. /** If true, also copies to the output directory dependent shared libraries (link and preload). */
  464. boolean copyLibs = false;
  465. /** Accumulates the various properties loaded from resources, files, command line options, etc. */
  466. Properties properties = null;
  467. /** The instance of the {@link ClassScanner} that fills up a {@link Collection} of {@link Class} objects to process. */
  468. ClassScanner classScanner = null;
  469. /** User specified environment variables to pass to the native compiler. */
  470. Map<String,String> environmentVariables = null;
  471. /** Contains additional command line options from the user for the native compiler. */
  472. Collection<String> compilerOptions = null;
  473. /** Splits argument with {@link File#pathSeparator} and appends result to paths of the {@link #classScanner}. */
  474. public Builder classPaths(String classPaths) {
  475. classPaths(classPaths == null ? null : classPaths.split(File.pathSeparator));
  476. return this;
  477. }
  478. /** Appends argument to the paths of the {@link #classScanner}. */
  479. public Builder classPaths(String ... classPaths) {
  480. classScanner.getClassLoader().addPaths(classPaths);
  481. return this;
  482. }
  483. /** Sets the {@link #outputDirectory} field to the argument. */
  484. public Builder outputDirectory(String outputDirectory) {
  485. outputDirectory(outputDirectory == null ? null : new File(outputDirectory));
  486. return this;
  487. }
  488. /** Sets the {@link #outputDirectory} field to the argument. */
  489. public Builder outputDirectory(File outputDirectory) {
  490. this.outputDirectory = outputDirectory;
  491. return this;
  492. }
  493. /** Sets the {@link #compile} field to the argument. */
  494. public Builder compile(boolean compile) {
  495. this.compile = compile;
  496. return this;
  497. }
  498. /** Sets the {@link #deleteJniFiles} field to the argument. */
  499. public Builder deleteJniFiles(boolean deleteJniFiles) {
  500. this.deleteJniFiles = deleteJniFiles;
  501. return this;
  502. }
  503. /** Sets the {@link #header} field to the argument. */
  504. public Builder header(boolean header) {
  505. this.header = header;
  506. return this;
  507. }
  508. /** Sets the {@link #copyLibs} field to the argument. */
  509. public Builder copyLibs(boolean copyLibs) {
  510. this.copyLibs = copyLibs;
  511. return this;
  512. }
  513. /** Sets the {@link #outputName} field to the argument. */
  514. public Builder outputName(String outputName) {
  515. this.outputName = outputName;
  516. return this;
  517. }
  518. /** Sets the {@link #jarPrefix} field to the argument. */
  519. public Builder jarPrefix(String jarPrefix) {
  520. this.jarPrefix = jarPrefix;
  521. return this;
  522. }
  523. /** Sets the {@link #properties} field to the ones loaded from resources for the specified platform. */
  524. public Builder properties(String platform) {
  525. if (platform != null) {
  526. properties = Loader.loadProperties(platform, null);
  527. }
  528. return this;
  529. }
  530. /** Adds all the properties of the argument to the {@link #properties} field. */
  531. public Builder properties(Properties properties) {
  532. if (properties != null) {
  533. for (Map.Entry e : properties.entrySet()) {
  534. property((String)e.getKey(), (String)e.getValue());
  535. }
  536. }
  537. return this;
  538. }
  539. /** Sets the {@link #properties} field to the ones loaded from the specified file. */
  540. public Builder propertyFile(String filename) throws IOException {
  541. propertyFile(filename == null ? null : new File(filename));
  542. return this;
  543. }
  544. /** Sets the {@link #properties} field to the ones loaded from the specified file. */
  545. public Builder propertyFile(File propertyFile) throws IOException {
  546. if (propertyFile == null) {
  547. return this;
  548. }
  549. FileInputStream fis = new FileInputStream(propertyFile);
  550. properties = new Properties();
  551. try {
  552. properties.load(new InputStreamReader(fis));
  553. } catch (NoSuchMethodError e) {
  554. properties.load(fis);
  555. }
  556. fis.close();
  557. return this;
  558. }
  559. /** Sets a property of the {@link #properties} field, in either "key=value" or "key:value" format. */
  560. public Builder property(String keyValue) {
  561. int equalIndex = keyValue.indexOf('=');
  562. if (equalIndex < 0) {
  563. equalIndex = keyValue.indexOf(':');
  564. }
  565. property(keyValue.substring(2, equalIndex),
  566. keyValue.substring(equalIndex+1));
  567. return this;
  568. }
  569. /** Sets a key/value pair property of the {@link #properties} field. */
  570. public Builder property(String key, String value) {
  571. if (key.length() > 0 && value.length() > 0) {
  572. properties.put(key, value);
  573. }
  574. return this;
  575. }
  576. /** Requests the {@link #classScanner} to add a class or all classes from a package.
  577. * A {@code null} argument indicates the unnamed package. */
  578. public Builder classesOrPackages(String ... classesOrPackages) throws IOException, ClassNotFoundException, NoClassDefFoundError {
  579. if (classesOrPackages == null) {
  580. classScanner.addPackage(null, true);
  581. } else for (String s : classesOrPackages) {
  582. classScanner.addClassOrPackage(s);
  583. }
  584. return this;
  585. }
  586. /** Sets the {@link #environmentVariables} field to the argument. */
  587. public Builder environmentVariables(Map<String,String> environmentVariables) {
  588. this.environmentVariables = environmentVariables;
  589. return this;
  590. }
  591. /** Appends arguments to the {@link #compilerOptions} field. */
  592. public Builder compilerOptions(String ... options) {
  593. if (options != null) {
  594. compilerOptions.addAll(Arrays.asList(options));
  595. }
  596. return this;
  597. }
  598. /**
  599. * Starts the build process and returns an array of {@link File} produced.
  600. *
  601. * @return the array of File produced
  602. * @throws IOException
  603. * @throws InterruptedException
  604. * @throws ParserException
  605. */
  606. public File[] build() throws IOException, InterruptedException, ParserException {
  607. if (classScanner.getClasses().isEmpty()) {
  608. return null;
  609. }
  610. List<File> outputFiles = new ArrayList<File>();
  611. Map<String, LinkedHashSet<Class>> map = new LinkedHashMap<String, LinkedHashSet<Class>>();
  612. for (Class c : classScanner.getClasses()) {
  613. if (Loader.getEnclosingClass(c) != c) {
  614. continue;
  615. }
  616. ClassProperties p = Loader.loadProperties(c, properties, false);
  617. if (!p.isLoaded()) {
  618. logger.warn("Could not load platform properties for " + c);
  619. continue;
  620. }
  621. String target = p.getProperty("target");
  622. if (target != null && !c.getName().equals(target)) {
  623. File f = parse(classScanner.getClassLoader().getPaths(), c);
  624. if (f != null) {
  625. outputFiles.add(f);
  626. }
  627. continue;
  628. }
  629. String libraryName = outputName != null ? outputName : p.getProperty("platform.library", "");
  630. if (libraryName.length() == 0) {
  631. continue;
  632. }
  633. LinkedHashSet<Class> classList = map.get(libraryName);
  634. if (classList == null) {
  635. map.put(libraryName, classList = new LinkedHashSet<Class>());
  636. }
  637. classList.addAll(p.getEffectiveClasses());
  638. }
  639. for (String libraryName : map.keySet()) {
  640. LinkedHashSet<Class> classSet = map.get(libraryName);
  641. Class[] classArray = classSet.toArray(new Class[classSet.size()]);
  642. File f = generateAndCompile(classArray, libraryName);
  643. if (f != null) {
  644. outputFiles.add(f);
  645. if (copyLibs) {
  646. // Do not copy library files from inherit properties ...
  647. ClassProperties p = Loader.loadProperties(classArray, properties, false);
  648. List<String> preloads = new ArrayList<String>();
  649. preloads.addAll(p.get("platform.preload"));
  650. preloads.addAll(p.get("platform.link"));
  651. // ... but we should use all the inherited paths!
  652. p = Loader.loadProperties(classArray, properties, true);
  653. File directory = f.getParentFile();
  654. for (String s : preloads) {
  655. URL[] urls = Loader.findLibrary(null, p, s, true);
  656. File fi;
  657. try {
  658. fi = new File(urls[0].toURI());
  659. } catch (Exception e) {
  660. continue;
  661. }
  662. File fo = new File(directory, fi.getName());
  663. if (fi.exists() && !outputFiles.contains(fo)) {
  664. logger.info("Copying " + fi);
  665. FileInputStream fis = new FileInputStream(fi);
  666. FileOutputStream fos = new FileOutputStream(fo);
  667. byte[] buffer = new byte[1024];
  668. int length;
  669. while ((length = fis.read(buffer)) != -1) {
  670. fos.write(buffer, 0, length);
  671. }
  672. fos.close();
  673. fis.close();
  674. outputFiles.add(fo);
  675. }
  676. }
  677. }
  678. }
  679. }
  680. File[] files = outputFiles.toArray(new File[outputFiles.size()]);
  681. if (jarPrefix != null && files.length > 0) {
  682. File jarFile = new File(jarPrefix + "-" + properties.get("platform") + ".jar");
  683. File d = jarFile.getParentFile();
  684. if (d != null && !d.exists()) {
  685. d.mkdir();
  686. }
  687. createJar(jarFile, outputDirectory == null ? classScanner.getClassLoader().getPaths() : null, files);
  688. }
  689. // reset the load flag to let users load compiled libraries
  690. System.setProperty("org.bytedeco.javacpp.loadlibraries", "true");
  691. return files;
  692. }
  693. /**
  694. * Simply prints out to the display the command line usage.
  695. */
  696. public static void printHelp() {
  697. String version = Builder.class.getPackage().getImplementationVersion();
  698. if (version == null) {
  699. version = "unknown";
  700. }
  701. System.out.println(
  702. "JavaCPP version " + version + "\n" +
  703. "Copyright (C) 2011-2016 Samuel Audet <samuel.audet@gmail.com>\n" +
  704. "Project site: https://github.com/bytedeco/javacpp");
  705. System.out.println();
  706. System.out.println("Usage: java -jar javacpp.jar [options] [class or package (suffixed with .* or .**)]");
  707. System.out.println();
  708. System.out.println("where options include:");
  709. System.out.println();
  710. System.out.println(" -classpath <path> Load user classes from path");
  711. System.out.println(" -d <directory> Output all generated files to directory");
  712. System.out.println(" -o <name> Output everything in a file named after given name");
  713. System.out.println(" -nocompile Do not compile or delete the generated source files");
  714. System.out.println(" -nodelete Do not delete generated C++ JNI files after compilation");
  715. System.out.println(" -header Generate header file with declarations of callbacks functions");
  716. System.out.println(" -copylibs Copy to output directory dependent libraries (link and preload)");
  717. System.out.println(" -jarprefix <prefix> Also create a JAR file named \"<prefix>-<platform>.jar\"");
  718. System.out.println(" -properties <resource> Load all properties from resource");
  719. System.out.println(" -propertyfile <file> Load all properties from file");
  720. System.out.println(" -D<property>=<value> Set property to value");
  721. System.out.println(" -Xcompiler <option> Pass option directly to compiler");
  722. System.out.println();
  723. }
  724. /**
  725. * The terminal shell interface to the Builder.
  726. *
  727. * @param args an array of arguments as described by {@link #printHelp()}
  728. * @throws Exception
  729. */
  730. public static void main(String[] args) throws Exception {
  731. boolean addedClasses = false;
  732. Builder builder = new Builder();
  733. for (int i = 0; i < args.length; i++) {
  734. if ("-help".equals(args[i]) || "--help".equals(args[i])) {
  735. printHelp();
  736. System.exit(0);
  737. } else if ("-classpath".equals(args[i]) || "-cp".equals(args[i]) || "-lib".equals(args[i])) {
  738. builder.classPaths(args[++i]);
  739. } else if ("-d".equals(args[i])) {
  740. builder.outputDirectory(args[++i]);
  741. } else if ("-o".equals(args[i])) {
  742. builder.outputName(args[++i]);
  743. } else if ("-cpp".equals(args[i]) || "-nocompile".equals(args[i])) {
  744. builder.compile(false);
  745. } else if ("-nodelete".equals(args[i])) {
  746. builder.deleteJniFiles(false);
  747. } else if ("-header".equals(args[i])) {
  748. builder.header(true);
  749. } else if ("-copylibs".equals(args[i])) {
  750. builder.copyLibs(true);
  751. } else if ("-jarprefix".equals(args[i])) {
  752. builder.jarPrefix(args[++i]);
  753. } else if ("-properties".equals(args[i])) {
  754. builder.properties(args[++i]);
  755. } else if ("-propertyfile".equals(args[i])) {
  756. builder.propertyFile(args[++i]);
  757. } else if (args[i].startsWith("-D")) {
  758. builder.property(args[i]);
  759. } else if ("-Xcompiler".equals(args[i])) {
  760. builder.compilerOptions(args[++i]);
  761. } else if (args[i].startsWith("-")) {
  762. builder.logger.error("Invalid option \"" + args[i] + "\"");
  763. printHelp();
  764. System.exit(1);
  765. } else {
  766. builder.classesOrPackages(args[i]);
  767. addedClasses = true;
  768. }
  769. }
  770. if (!addedClasses) {
  771. builder.classesOrPackages((String[])null);
  772. }
  773. builder.build();
  774. }
  775. }