Skip to content

Commit a51c022

Browse files
author
Federico Fissore
committed
NPM integration first implementation: downloads dependencies into a target folder and collect them appending version to the folder name
1 parent fccfca8 commit a51c022

13 files changed

+278
-3
lines changed

app/lib/jackson-core-lgpl-1.9.12.jar

232 KB
Binary file not shown.
767 KB
Binary file not shown.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package processing.app.packages;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
6+
public interface DependenciesResolver {
7+
8+
void resolveAndDownload(File libFolder, File targetFolder) throws IOException;
9+
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package processing.app.packages;
2+
3+
import java.io.IOException;
4+
import java.io.Writer;
5+
6+
public interface LibraryConverter {
7+
8+
void convert(Library library, Writer writer) throws IOException;
9+
10+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package processing.app.packages.npm;
2+
3+
import org.apache.commons.exec.CommandLine;
4+
import org.codehaus.jackson.JsonNode;
5+
import org.codehaus.jackson.map.ObjectMapper;
6+
import processing.app.helpers.FileUtils;
7+
import processing.app.packages.DependenciesResolver;
8+
import processing.app.packages.Library;
9+
import processing.app.packages.LibraryConverter;
10+
import processing.app.tools.ExternalProcessExecutor;
11+
12+
import java.io.File;
13+
import java.io.FileFilter;
14+
import java.io.FileWriter;
15+
import java.io.IOException;
16+
17+
public class NPMDependenciesResolver implements DependenciesResolver {
18+
19+
private final String pathToNpmExec;
20+
private final LibraryConverter libraryConverter;
21+
private final ObjectMapper objectMapper;
22+
23+
public NPMDependenciesResolver(String pathToNpmExec, LibraryConverter libraryConverter) {
24+
this.pathToNpmExec = pathToNpmExec;
25+
this.libraryConverter = libraryConverter;
26+
this.objectMapper = new ObjectMapper();
27+
}
28+
29+
@Override
30+
public void resolveAndDownload(File libFolder, File targetFolder) throws IOException {
31+
Library library = Library.create(libFolder);
32+
File packageJson = new File(targetFolder, "package.json");
33+
34+
FileWriter fw = null;
35+
try {
36+
fw = new FileWriter(packageJson);
37+
libraryConverter.convert(library, fw);
38+
} finally {
39+
if (fw != null) {
40+
fw.close();
41+
}
42+
}
43+
44+
ExternalProcessExecutor executor = new ExternalProcessExecutor();
45+
executor.setWorkingDirectory(targetFolder);
46+
CommandLine parse = CommandLine.parse(pathToNpmExec + " install -d");
47+
executor.execute(parse);
48+
49+
recursivelyMoveNodeModulesToUpperFolder(targetFolder, targetFolder);
50+
}
51+
52+
private void recursivelyMoveNodeModulesToUpperFolder(File currentFolder, File targetFolder) throws IOException {
53+
File subNodeModules = new File(currentFolder, "node_modules");
54+
if (subNodeModules.exists() && subNodeModules.isDirectory()) {
55+
recursivelyMoveNodeModulesToUpperFolder(subNodeModules, targetFolder);
56+
FileUtils.recursiveDelete(subNodeModules);
57+
return;
58+
}
59+
60+
File[] nodeModules = currentFolder.listFiles(new FileFilter() {
61+
@Override
62+
public boolean accept(File file) {
63+
return file.isDirectory() && !file.isHidden();
64+
}
65+
});
66+
67+
for (File nodeModule : nodeModules) {
68+
subNodeModules = new File(nodeModule, "node_modules");
69+
if (subNodeModules.exists() && subNodeModules.isDirectory()) {
70+
recursivelyMoveNodeModulesToUpperFolder(subNodeModules, targetFolder);
71+
FileUtils.recursiveDelete(subNodeModules);
72+
}
73+
JsonNode packageJson = objectMapper.readTree(new File(nodeModule, "package.json"));
74+
String version = packageJson.get("version").getTextValue();
75+
File dest = new File(targetFolder, nodeModule.getName() + "-" + version);
76+
if (!dest.exists()) {
77+
if (!nodeModule.renameTo(dest)) {
78+
throw new IOException("Unable to move " + nodeModule + " to " + dest);
79+
}
80+
} else {
81+
FileUtils.recursiveDelete(nodeModule);
82+
}
83+
}
84+
}
85+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package processing.app.packages.npm;
2+
3+
import org.codehaus.jackson.map.ObjectMapper;
4+
import processing.app.packages.Library;
5+
import processing.app.packages.LibraryConverter;
6+
7+
import java.io.IOException;
8+
import java.io.Writer;
9+
import java.util.HashMap;
10+
import java.util.Locale;
11+
import java.util.Map;
12+
13+
public class NPMLibraryConverter implements LibraryConverter {
14+
15+
@Override
16+
public void convert(Library library, Writer writer) throws IOException {
17+
Map<String, Object> packageJson = new HashMap<String, Object>();
18+
packageJson.put("name", library.getName());
19+
packageJson.put("version", "1.0.0");
20+
21+
Map<String, Object> dependencies = new HashMap<String, Object>();
22+
for (String dependency : library.getDependencies()) {
23+
String name;
24+
String version = "*";
25+
if (dependency.contains("(")) {
26+
name = dependency.substring(0, dependency.indexOf("(")).trim().toLowerCase(Locale.US);
27+
version = dependency.substring(dependency.indexOf("(") + 1, dependency.indexOf(")"));
28+
} else {
29+
name = dependency.trim().toLowerCase(Locale.US);
30+
}
31+
dependencies.put(name, version);
32+
}
33+
34+
packageJson.put("dependencies", dependencies);
35+
36+
ObjectMapper objectMapper = new ObjectMapper();
37+
objectMapper.writeValue(writer, packageJson);
38+
}
39+
}

app/src/processing/app/tools/ExternalProcessExecutor.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,44 @@
1212
*/
1313
public class ExternalProcessExecutor extends DefaultExecutor {
1414

15-
public ExternalProcessExecutor(final OutputStream os) {
15+
public ExternalProcessExecutor() {
16+
this(false);
17+
}
18+
19+
public ExternalProcessExecutor(boolean debug) {
20+
this(null, null, debug);
21+
}
22+
23+
public ExternalProcessExecutor(OutputStream stdout) {
24+
this(stdout, null, false);
25+
}
26+
27+
public ExternalProcessExecutor(final OutputStream stdout, final OutputStream stderr, final boolean debug) {
1628
this.setStreamHandler(new ExecuteStreamHandler() {
1729
@Override
1830
public void setProcessInputStream(OutputStream outputStream) throws IOException {
1931
}
2032

2133
@Override
2234
public void setProcessErrorStream(InputStream inputStream) throws IOException {
35+
pipe(inputStream, stderr);
2336
}
2437

2538
@Override
2639
public void setProcessOutputStream(InputStream inputStream) throws IOException {
40+
pipe(inputStream, stdout);
41+
}
42+
43+
private void pipe(InputStream inputStream, OutputStream output) throws IOException {
2744
byte[] buf = new byte[4096];
28-
int bytes = -1;
45+
int bytes;
2946
while ((bytes = inputStream.read(buf)) != -1) {
30-
os.write(buf, 0, bytes);
47+
if (debug) {
48+
System.out.println(new String(buf, 0, bytes));
49+
}
50+
if (output != null) {
51+
output.write(buf, 0, bytes);
52+
}
3153
}
3254
}
3355

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package processing.app.packages.npm;
2+
3+
import org.junit.After;
4+
import org.junit.Before;
5+
import org.junit.Test;
6+
import processing.app.helpers.FileUtils;
7+
import processing.app.packages.DependenciesResolver;
8+
9+
import java.io.File;
10+
import java.io.FileFilter;
11+
12+
import static org.junit.Assert.assertTrue;
13+
14+
public class NPMDependenciesResolverTest {
15+
16+
private DependenciesResolver resolver;
17+
private File targetFolder;
18+
19+
@Before
20+
public void setUp() throws Exception {
21+
File parent = new File(NPMDependenciesResolverTest.class.getResource("/").getFile());
22+
String pathToNpmExec = new File(parent, "../../build/linux/work/nodejs/bin/npm").getAbsolutePath();
23+
24+
resolver = new NPMDependenciesResolver(pathToNpmExec, new NPMLibraryConverter());
25+
26+
targetFolder = new File(System.getProperty("java.io.tmpdir"), "arduino_" + Math.round(Math.random() * 100000));
27+
targetFolder.mkdirs();
28+
}
29+
30+
@After
31+
public void tearDown() throws Exception {
32+
FileUtils.recursiveDelete(targetFolder);
33+
}
34+
35+
@Test
36+
public void shouldDownloadAndOrganizeDependencies() throws Exception {
37+
File libFolder = new File(NPMLibraryConverterTest.class.getResource("/processing/app/packages/test_lib_node_modules/library.properties").getFile()).getParentFile();
38+
39+
resolver.resolveAndDownload(libFolder, targetFolder);
40+
41+
assertTrue(listFoldersStartingWith("async").length >= 1);
42+
assertTrue(listFoldersStartingWith("orientdb").length >= 1);
43+
assertTrue(listFoldersStartingWith("by-mocha").length >= 1);
44+
assertTrue(listFoldersStartingWith("grunt-mocha-cli").length >= 1);
45+
46+
File[] folders = targetFolder.listFiles(new FileFilter() {
47+
@Override
48+
public boolean accept(File file) {
49+
return file.isDirectory();
50+
}
51+
});
52+
53+
// modules we depend to have sub dependencies
54+
assertTrue(folders.length > 4);
55+
}
56+
57+
private File[] listFoldersStartingWith(final String prefix) {
58+
return targetFolder.listFiles(new FileFilter() {
59+
@Override
60+
public boolean accept(File file) {
61+
return file.isDirectory() && file.getName().startsWith(prefix);
62+
}
63+
});
64+
}
65+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package processing.app.packages.npm;
2+
3+
import org.junit.Test;
4+
import processing.app.packages.Library;
5+
6+
import java.io.File;
7+
import java.io.StringWriter;
8+
9+
import static org.junit.Assert.assertEquals;
10+
11+
public class NPMLibraryConverterTest {
12+
13+
@Test
14+
public void shouldConvertToPackageJSON() throws Exception {
15+
File libFolder = new File(NPMLibraryConverterTest.class.getResource("/processing/app/packages/test_lib/library.properties").getFile()).getParentFile();
16+
Library library = Library.create(libFolder);
17+
18+
NPMLibraryConverter converter = new NPMLibraryConverter();
19+
StringWriter writer = new StringWriter();
20+
converter.convert(library, writer);
21+
22+
assertEquals("{\"dependencies\":{\"ethernet\":\">=1.0\",\"by-mocha\":\"*\",\"spi\":\"*\",\"servo\":\"=3.0\",\"grunt-mocha-cli\":\"*\"},\"name\":\"WebServer\",\"version\":\"1.0.0\"}", writer.toString());
23+
}
24+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name=WebServer
2+
author=cmaglie
3+
email=Cristian Maglie <[email protected]>
4+
sentence=A library that makes coding Webserver a breeze.
5+
paragraph=Supports HTTP1.1 and you can do GET and POST.
6+
url=http://asdasd.com/
7+
architectures=avr,sam
8+
version=1.0
9+
dependencies=Ethernet (>=1.0),Servo (=2.0),Servo (=3.0),SPI,,by-mocha,grunt-mocha-cli
10+
core-dependencies=arduino (>=1.5.0)

app/test/processing/app/packages/test_lib/src/keepme.txt

Whitespace-only changes.

app/test/processing/app/packages/test_lib_node_modules/library.properties

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/test/processing/app/packages/test_lib_node_modules/src/keepme.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)