Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Enable zipped sketch with embedded libraries #6114

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/src/processing/app/Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,8 @@ public void handleOpenPrompt() throws Exception {
fd.setFilenameFilter(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".ino")
|| name.toLowerCase().endsWith(".pde");
|| name.toLowerCase().endsWith(".pde")
|| name.toLowerCase().endsWith(".inz");
}
});

Expand Down
14 changes: 8 additions & 6 deletions app/src/processing/app/Editor.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public boolean test(SketchController sketch) {

static volatile AbstractMonitor serialMonitor;
static AbstractMonitor serialPlotter;

final EditorHeader header;
EditorStatus status;
EditorConsole console;
Expand Down Expand Up @@ -247,7 +247,7 @@ public void windowDeactivated(WindowEvent e) {

//PdeKeywords keywords = new PdeKeywords();
//sketchbook = new Sketchbook(this);

buildMenuBar();

// For rev 0120, placing things inside a JPanel
Expand Down Expand Up @@ -1862,7 +1862,9 @@ protected boolean handleOpenInternal(File sketchFile) {
File file = Sketch.checkSketchFile(sketchFile);

if (file == null) {
if (!fileName.endsWith(".ino") && !fileName.endsWith(".pde")) {
if (fileName.endsWith(".inz")) {
file = sketchFile;
} else if (!fileName.endsWith(".ino") && !fileName.endsWith(".pde")) {

Base.showWarning(tr("Bad file selected"), tr("Arduino can only open its own sketches\n" +
"and other files ending in .ino or .pde"), null);
Expand Down Expand Up @@ -2288,7 +2290,7 @@ public void handleSerial() {
return;
}
}

if (serialMonitor != null) {
// The serial monitor already exists

Expand Down Expand Up @@ -2385,7 +2387,7 @@ public void handleSerial() {
} while (serialMonitor.requiresAuthorization() && !success);

}

public void handlePlotter() {
if(serialMonitor != null) {
if(serialMonitor.isClosed()) {
Expand All @@ -2395,7 +2397,7 @@ public void handlePlotter() {
return;
}
}

if (serialPlotter != null) {
// The serial plotter already exists

Expand Down
85 changes: 83 additions & 2 deletions arduino-core/src/processing/app/Sketch.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.io.FileOutputStream;

import cc.arduino.files.DeleteFilesOnShutdown;
import processing.app.helpers.FileUtils;
import processing.app.tools.ZipDeflater;

import org.apache.commons.compress.utils.IOUtils;

import static processing.app.I18n.tr;

Expand All @@ -17,8 +24,10 @@
*/
public class Sketch {
public static final String DEFAULT_SKETCH_EXTENSION = "ino";
public static final String DEFAULT_SKETCH_EXTENSION_COMPRESSED = "inoz";
public static final List<String> OLD_SKETCH_EXTENSIONS = Arrays.asList("pde");
public static final List<String> SKETCH_EXTENSIONS = Stream.concat(Stream.of(DEFAULT_SKETCH_EXTENSION), OLD_SKETCH_EXTENSIONS.stream()).collect(Collectors.toList());
public static final List<String> DEFAULT_SKETCH_EXTENSIONS = Stream.concat(Stream.of(DEFAULT_SKETCH_EXTENSION), Stream.of(DEFAULT_SKETCH_EXTENSION_COMPRESSED)).collect(Collectors.toList());
public static final List<String> SKETCH_EXTENSIONS = Stream.concat(DEFAULT_SKETCH_EXTENSIONS.stream(), OLD_SKETCH_EXTENSIONS.stream()).collect(Collectors.toList());
public static final List<String> OTHER_ALLOWED_EXTENSIONS = Arrays.asList("c", "cpp", "h", "hh", "hpp", "s");
public static final List<String> EXTENSIONS = Stream.concat(SKETCH_EXTENSIONS.stream(), OTHER_ALLOWED_EXTENSIONS.stream()).collect(Collectors.toList());

Expand All @@ -27,6 +36,8 @@ public class Sketch {
*/
private File folder;

private File compressedSketch = null;

private List<SketchFile> files = new ArrayList<>();

private File buildPath;
Expand All @@ -50,10 +61,70 @@ public int compare(SketchFile x, SketchFile y) {
* Any file inside the sketch directory.
*/
Sketch(File file) throws IOException {
folder = file.getParentFile();
if (file.getName().endsWith("inz")) {
//extract it in a temp folder and assign the
compressedSketch = file;
File tmpFolder = FileUtils.createTempFolder();
ZipDeflater zipDeflater = new ZipDeflater(file, tmpFolder);
zipDeflater.deflate();
String basename = FileUtils.splitFilename(file.getName()).basename;
folder = new File(tmpFolder, basename);
} else {
folder = file.getParentFile();
}
files = listSketchFiles(true);
}

public void buildZip(File dir, String sofar,
ZipOutputStream zos) throws IOException {
String files[] = dir.list();
if (files == null) {
throw new IOException("Unable to list files from " + dir);
}
for (int i = 0; i < files.length; i++) {
if (files[i].equals(".") ||
files[i].equals("..")) continue;

File sub = new File(dir, files[i]);
String nowfar = (sofar == null) ?
files[i] : (sofar + "/" + files[i]);

if (sub.isDirectory()) {
// directories are empty entries and have / at the end
ZipEntry entry = new ZipEntry(nowfar + "/");
//System.out.println(entry);
zos.putNextEntry(entry);
zos.closeEntry();
buildZip(sub, nowfar, zos);

} else {
ZipEntry entry = new ZipEntry(nowfar);
entry.setTime(sub.lastModified());
zos.putNextEntry(entry);
zos.write(loadBytesRaw(sub));
zos.closeEntry();
}
}
}

static public byte[] loadBytesRaw(File file) throws IOException {
int size = (int) file.length();
FileInputStream input = null;
try {
input = new FileInputStream(file);
byte buffer[] = new byte[size];
int offset = 0;
int bytesRead;
while ((bytesRead = input.read(buffer, offset, size - offset)) != -1) {
offset += bytesRead;
if (bytesRead == 0) break;
}
return buffer;
} finally {
IOUtils.closeQuietly(input);
}
}

static public File checkSketchFile(File file) {
// check to make sure that this .pde file is
// in a folder of the same name
Expand Down Expand Up @@ -137,6 +208,13 @@ public void save() throws IOException {
if (file.isModified())
file.save();
}
String basename = files.get(0).getPrettyName();
if (compressedSketch != null) {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(compressedSketch));
buildZip(folder, basename, zos);
zos.flush();
IOUtils.closeQuietly(zos);
}
}

public int getCodeCount() {
Expand All @@ -151,6 +229,9 @@ public SketchFile[] getFiles() {
* Returns a file object for the primary .pde of this sketch.
*/
public SketchFile getPrimaryFile() {
if (compressedSketch != null) {
return new SketchFile(this, compressedSketch);
}
return files.get(0);
}

Expand Down
109 changes: 109 additions & 0 deletions arduino-core/src/processing/app/tools/ZipDeflater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package processing.app.tools;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import org.apache.commons.compress.utils.IOUtils;
import processing.app.helpers.FileUtils;

public class ZipDeflater {

private final ZipFile zipFile;
private final File destFolder;
private final Random random;
private final File file;

public ZipDeflater(File file, File destFolder) throws ZipException, IOException {
this.file = file;
this.destFolder = destFolder;
this.zipFile = new ZipFile(file);
this.random = new Random();
}

public void deflate() throws IOException {
String tmpFolderName = folderNameFromZip() + random.nextInt(1000000);

File tmpFolder = new File(destFolder, tmpFolderName);

if (!tmpFolder.mkdir()) {
throw new IOException("Unable to create folder " + tmpFolderName);
}

Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
ensureFoldersOfEntryExist(tmpFolder, entry);
File entryFile = new File(tmpFolder, entry.getName());
if (entry.isDirectory()) {
entryFile.mkdir();
} else {
FileOutputStream fos = null;
InputStream zipInputStream = null;
try {
fos = new FileOutputStream(entryFile);
zipInputStream = zipFile.getInputStream(entry);
byte[] buffer = new byte[1024 * 4];
int len = -1;
while ((len = zipInputStream.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} finally {
IOUtils.closeQuietly(fos);
IOUtils.closeQuietly(zipInputStream);
}
}
}

deleteUndesiredFoldersAndFiles(tmpFolder);

// Test.zip may or may not contain Test folder. If it does, we keep it. If not, we use zip name.
ensureOneLevelFolder(tmpFolder);
}

private void deleteUndesiredFoldersAndFiles(File folder) {
for (File file : folder.listFiles()) {
if (file.isDirectory() && "__MACOSX".equals(file.getName())) {
FileUtils.recursiveDelete(file);
} else if (file.getName().startsWith(".")) {
FileUtils.recursiveDelete(file);
}
}
}

private void ensureFoldersOfEntryExist(File folder, ZipEntry entry) {
String[] parts = entry.getName().split("/");
File current = folder;
for (int i = 0; i < parts.length - 1; i++) {
current = new File(current, parts[i]);
current.mkdir();
}
}

private void ensureOneLevelFolder(File folder) {
File[] files = folder.listFiles();

if (files.length != 1) {
folder.renameTo(new File(folder.getParentFile(), folderNameFromZip()));
return;
}

files[0].renameTo(new File(folder.getParentFile(), files[0].getName()));
FileUtils.recursiveDelete(folder);
}

private String folderNameFromZip() {
String filename = file.getName();
if (filename.lastIndexOf(".") != -1) {
filename = filename.substring(0, filename.lastIndexOf("."));
}
return filename;
}

}