diff --git a/JavaBuilder.iml b/JavaBuilder.iml
index 8c5afa8..7c0db3a 100644
--- a/JavaBuilder.iml
+++ b/JavaBuilder.iml
@@ -34,10 +34,9 @@
-
-
\ No newline at end of file
+
diff --git a/LICENSE b/LICENSE
index 160caef..5315a53 100644
--- a/LICENSE
+++ b/LICENSE
@@ -14,13 +14,6 @@
Copyright 2010, dorkbox, llc
- - FastMD5 - LGPL v3 License
- http://www.twmacinta.com/myjava/fast_md5.php
- Copyright 1996, Santeri Paavolainen, Helsinki Finland
- Many changes Copyright 2002 - 2010 Timothy W Macinta
- Originally written by Santeri Paavolainen, Helsinki Finland 1996
-
-
- FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
http://commons.apache.org/proper/commons-io/
Copyright 2013, ASF
diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3
deleted file mode 100644
index 65c5ca8..0000000
--- a/LICENSE.LGPLv3
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/src/dorkbox/build/Project.java b/src/dorkbox/build/Project.java
index d5eee73..4da1cc6 100644
--- a/src/dorkbox/build/Project.java
+++ b/src/dorkbox/build/Project.java
@@ -28,7 +28,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.bouncycastle.crypto.digests.MD5Digest;
import org.slf4j.Logger;
import com.esotericsoftware.kryo.Kryo;
@@ -37,16 +36,15 @@ import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.minlog.Log;
import com.esotericsoftware.wildcard.Paths;
-import com.twmacinta.util.MD5;
import dorkbox.BuildOptions;
import dorkbox.BuildVersion;
import dorkbox.Builder;
import dorkbox.build.util.BuildLog;
+import dorkbox.build.util.Hash;
import dorkbox.build.util.OutputFile;
import dorkbox.build.util.ShutdownHook;
import dorkbox.license.License;
-import dorkbox.util.Base64Fast;
import dorkbox.util.FileUtil;
import dorkbox.util.OS;
import dorkbox.util.SerializationManager;
@@ -160,7 +158,7 @@ class Project> {
try {
String oldHash = Builder.settings.get("BUILD", String.class);
- String hashedContents = generateChecksums(paths);
+ String hashedContents = Hash.generateChecksums(paths);
if (oldHash != null) {
if (!oldHash.equals(hashedContents)) {
@@ -185,6 +183,8 @@ class Project> {
} catch (IOException e) {
e.printStackTrace();
}
+
+ Hash.forceRebuildAll = forceRebuildAll;
}
public static
@@ -235,7 +235,6 @@ class Project> {
// used to make sure licenses are called in the correct spot
private transient boolean calledLicenseBefore = false;
- transient Paths checksumPaths = new Paths();
protected List licenses = new ArrayList();
protected BuildOptions buildOptions;
@@ -259,6 +258,8 @@ class Project> {
// true if we should rebuild this project
boolean forceRebuild = false;
+ protected transient Hash hash;
+
/**
* Temporary projects are always built, but not always exported to maven (this is controlled by the parent, non-temp project
* recursively)
@@ -341,6 +342,8 @@ class Project> {
this.stagingDir = FileUtil.normalize(STAGING + File.separator + lowerCase_outputDir);
// must call this method, because it's not overridden by jar type
outputFile0(new File(this.stagingDir.getParentFile(), this.name + getExtension()).getAbsolutePath(), null);
+
+ hash = new Hash(projectName, buildOptions);
}
/**
@@ -522,7 +525,7 @@ class Project> {
*/
public
T depends(final Paths dependencies) {
- checksumPaths.add(dependencies);
+ hash.add(dependencies);
sourceDependencies.add(dependencies);
return (T) this;
@@ -793,111 +796,6 @@ class Project> {
}
}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////
-//// CHECKSUM LOGIC
-///////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Add a path to be checksum'd.
- */
- @SuppressWarnings("WeakerAccess")
- public final
- void checksum(Paths path) {
- this.checksumPaths.add(path);
- }
-
- /**
- * @return true if the checksums for path match the saved checksums and the jar file exists, false if the check failed and the
- * project needs to rebuild
- */
- boolean verifyChecksums() throws IOException {
- if (forceRebuildAll || this.buildOptions.compiler.forceRebuild) {
- return false;
- }
-
- // check to see if our SOURCES *and check-summed files* have changed.
- String hashedContents = generateChecksums(this.checksumPaths);
- String checkContents = Builder.settings.get(this.name, String.class);
-
- return hashedContents != null && hashedContents.equals(checkContents);
- }
-
- /**
- * Saves the checksums for a given path
- */
- void saveChecksums() throws IOException {
- // by default, we save the build. When building a 'test' build, we opt to NOT save the build hashes, so that a 'normal' build
- // will then compile.
- if (!buildOptions.compiler.saveBuild) {
- return;
- }
-
- // hash/save the sources *and check-summed files* files
- String hashedContents = generateChecksums(this.checksumPaths);
- Builder.settings.save(this.name, hashedContents);
- }
-
- /**
- * Generates checksums for the given path
- */
- public static
- String generateChecksum(File file) throws IOException {
- synchronized (Project.class) {
- // calculate the hash of file
- boolean found = false;
- if (file.isFile() && file.canRead()) {
- found = true;
- }
-
- if (!found) {
- return null;
- }
-
- byte[] hashBytes = MD5.getHash(file);
-
- return Base64Fast.encodeToString(hashBytes, false);
- }
- }
-
- /**
- * Generates checksums for the given path
- */
- public static
- String generateChecksums(Paths... paths) throws IOException {
- synchronized (Project.class) {
- // calculate the hash of all the files in the source path
- Set names = new HashSet(64);
-
- for (Paths path : paths) {
- names.addAll(path.getPaths());
- }
-
- // hash of hash of files. faster than using java to hash files
- MD5Digest md5_digest = new MD5Digest();
-
- boolean found = false;
- for (String name : names) {
- File file = new File(name);
- if (file.isFile() && file.canRead()) {
- found = true;
-
- byte[] hashBytes = MD5.getHash(file);
- md5_digest.update(hashBytes, 0, hashBytes.length);
- }
- }
-
- if (!found) {
- return null;
- }
-
- byte[] hashBytes = new byte[md5_digest.getDigestSize()];
- md5_digest.doFinal(hashBytes, 0);
-
- return Base64Fast.encodeToString(hashBytes, false);
- }
- }
-
public
T version(BuildVersion version) {
this.version = version;
diff --git a/src/dorkbox/build/ProjectGwt.java b/src/dorkbox/build/ProjectGwt.java
index 98db6cd..c9fa98d 100644
--- a/src/dorkbox/build/ProjectGwt.java
+++ b/src/dorkbox/build/ProjectGwt.java
@@ -110,7 +110,7 @@ class ProjectGwt extends Project {
if (srcDir.endsWith("src")) {
String parent = new File(srcDir).getAbsoluteFile()
.getParent();
- checksum(new Paths(parent));
+ hash.add(parent);
}
return sourcePath(new Paths(srcDir, "./"));
@@ -356,16 +356,15 @@ class ProjectGwt extends Project {
*
* @return true if the checksums for path match the saved checksums and the jar file exists
*/
- @Override
boolean verifyChecksums() throws IOException {
- boolean sourceHashesSame = super.verifyChecksums();
+ boolean sourceHashesSame = hash.verifyChecksums();
if (!sourceHashesSame) {
return false;
}
// if the sources are the same, check the output dir
if (this.stagingDir.exists()) {
- String dirChecksum = generateChecksum(this.stagingDir);
+ String dirChecksum = hash.generateChecksum(this.stagingDir);
String checkContents = Builder.settings.get(this.stagingDir.getAbsolutePath(), String.class);
return dirChecksum != null && dirChecksum.equals(checkContents);
@@ -377,7 +376,6 @@ class ProjectGwt extends Project {
/**
* GWT only cares about the output dir (it doesn't make jars for compiling) Saves the checksums for a given path
*/
- @Override
void saveChecksums() throws IOException {
// by default, we save the build. When building a 'test' build, we opt to NOT save the build hashes, so that a 'normal' build
// will then compile.
@@ -385,11 +383,11 @@ class ProjectGwt extends Project {
return;
}
- super.saveChecksums();
+ hash.saveChecksums();
// hash/save the output files (if there are any)
if (this.stagingDir.exists()) {
- String fileChecksum = generateChecksum(this.stagingDir);
+ String fileChecksum = hash.generateChecksum(this.stagingDir);
Builder.settings.save(this.stagingDir.getAbsolutePath(), fileChecksum);
}
}
diff --git a/src/dorkbox/build/ProjectJar.java b/src/dorkbox/build/ProjectJar.java
index afb8644..9ec269c 100644
--- a/src/dorkbox/build/ProjectJar.java
+++ b/src/dorkbox/build/ProjectJar.java
@@ -88,9 +88,9 @@ public class ProjectJar extends Project {
public
ProjectJar outputFileNoWarn(final String outputFile, final String outputSourceFile) {
- this.checksumPaths.addFile(outputFile);
+ hash.add(outputFile);
if (outputSourceFile != null) {
- this.checksumPaths.addFile(outputSourceFile);
+ hash.add(outputSourceFile);
}
return super.outputFile(outputFile, outputSourceFile);
diff --git a/src/dorkbox/build/ProjectJava.java b/src/dorkbox/build/ProjectJava.java
index aed7441..251ca4d 100644
--- a/src/dorkbox/build/ProjectJava.java
+++ b/src/dorkbox/build/ProjectJava.java
@@ -98,7 +98,7 @@ class ProjectJava extends Project {
this.sourcePaths.add(sourcePaths);
// ALWAYS add the source paths to be checksumed!
- checksum(sourcePaths);
+ hash.add(sourcePaths);
return this;
}
@@ -110,7 +110,7 @@ class ProjectJava extends Project {
ProjectJava sourcePath(String srcDir) {
if (srcDir.endsWith("src")) {
String parent = new File(srcDir).getAbsoluteFile().getParent();
- checksum(new Paths(parent));
+ hash.add(new Paths(parent));
}
return sourcePath(new Paths(srcDir, "./"));
@@ -217,7 +217,7 @@ class ProjectJava extends Project {
// also, we DO NOT check jar versions/etc here (that happens later)
// if true, this means that the files ARE the same and they have not changed
- final boolean b = project.verifyChecksums();
+ final boolean b = project.hash.verifyChecksums();
shouldBuild |= !b;
}
}
@@ -905,14 +905,13 @@ class ProjectJava extends Project {
* @return true if the checksums for path match the saved checksums. If there is a JAR file, it also checks to see if it is built &
* matches the saved checksums. If it's a temp project (and specifies a jar) the jarChecksum is ignored (so only checksums based on source code changes)
*/
- @Override
boolean verifyChecksums() throws IOException {
// if temporary + we override the status, we ALWAYS build it
if (this.temporary && this.overrideTemporary) {
return false;
}
- boolean sourceHashesSame = super.verifyChecksums();
+ boolean sourceHashesSame = hash.verifyChecksums();
if (!sourceHashesSame) {
return false;
}
@@ -926,7 +925,7 @@ class ProjectJava extends Project {
final File originalOutputFile = this.outputFile.getOriginal();
if (originalOutputFile.canRead()) {
- String jarChecksum = generateChecksum(originalOutputFile);
+ String jarChecksum = hash.generateChecksum(originalOutputFile);
String checkContents = Builder.settings.get(this.name + ":" + originalOutputFile.getAbsolutePath(), String.class);
boolean outputFileGood = jarChecksum != null && jarChecksum.equals(checkContents);
@@ -939,7 +938,7 @@ class ProjectJava extends Project {
final File originalOutputFileSource = this.outputFile.getSourceOriginal();
// now check the src.zip file (if there was one).
- jarChecksum = generateChecksum(originalOutputFileSource);
+ jarChecksum = hash.generateChecksum(originalOutputFileSource);
checkContents = Builder.settings.get(this.name + ":" + originalOutputFileSource.getAbsolutePath(), String.class);
return jarChecksum != null && jarChecksum.equals(checkContents);
@@ -961,7 +960,6 @@ class ProjectJava extends Project {
* Saves the checksums for a given path - PER PROJECT (otherwise updating a jar in one place, and saving it's checksum, will verify
* it everywhere else)
*/
- @Override
void saveChecksums() throws IOException {
// by default, we save the build. When building a 'test' build, we opt to NOT save the build hashes, so that a 'normal' build
// will then compile.
@@ -969,21 +967,21 @@ class ProjectJava extends Project {
return;
}
- super.saveChecksums();
+ hash.saveChecksums();
// when we verify checksums, we verify the ORIGINAL (if there is version info) -- and when we SAVE checksums, we save the NEW version
final File currentOutputFile = this.outputFile.get();
// hash/save the jar file (if there was one)
if (currentOutputFile.exists()) {
- String fileChecksum = generateChecksum(currentOutputFile);
+ String fileChecksum = hash.generateChecksum(currentOutputFile);
Builder.settings.save(this.name + ":" + currentOutputFile.getAbsolutePath(), fileChecksum);
if (this.jarable != null && this.jarable.includeSourceAsSeparate) {
final File currentOutputFileSource = this.outputFile.getSource();
// now check the src.zip file (if there was one).
- fileChecksum = generateChecksum(currentOutputFileSource);
+ fileChecksum = hash.generateChecksum(currentOutputFileSource);
Builder.settings.save(this.name + ":" + currentOutputFileSource.getAbsolutePath(), fileChecksum);
}
diff --git a/src/dorkbox/build/util/Hash.java b/src/dorkbox/build/util/Hash.java
new file mode 100644
index 0000000..8aab642
--- /dev/null
+++ b/src/dorkbox/build/util/Hash.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2012 dorkbox, llc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dorkbox.build.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.esotericsoftware.wildcard.Paths;
+
+import dorkbox.BuildOptions;
+import dorkbox.Builder;
+import dorkbox.build.Project;
+import dorkbox.util.Base64Fast;
+import dorkbox.util.IO;
+
+/**
+ * CHECKSUM LOGIC
+ */
+@SuppressWarnings({"Convert2Diamond", "AnonymousHasLambdaAlternative"})
+public
+class Hash {
+
+ private static final ThreadLocal digestThreadLocal = new ThreadLocal() {
+ @Override
+ protected
+ MessageDigest initialValue() {
+ try {
+ return MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException ignored) {
+ // will never happen, since SHA1 is part of java.
+ return null;
+ }
+ }
+ };
+
+
+ // set by Project.java
+ public static boolean forceRebuildAll = false;
+
+ private transient Paths checksumPaths = new Paths();
+ private final String projectName;
+ private BuildOptions buildOptions;
+
+ public
+ Hash(final String projectName, BuildOptions buildOptions) {
+ this.projectName = projectName;
+ this.buildOptions = buildOptions;
+ }
+
+ /**
+ * Add paths to be checksum'd.
+ */
+ public
+ void add(final Paths paths) {
+ this.checksumPaths.add(paths);
+ }
+
+ public
+ void add(final String file) {
+ this.checksumPaths.addFile(file);
+ }
+
+ /**
+ * @return true if the checksums for path match the saved checksums and the jar file exists, false if the check failed and the
+ * project needs to rebuild
+ */
+ public
+ boolean verifyChecksums() throws IOException {
+ if (forceRebuildAll || this.buildOptions.compiler.forceRebuild) {
+ return false;
+ }
+
+ // check to see if our SOURCES *and check-summed files* have changed.
+ String hashedContents = generateChecksums(this.checksumPaths);
+ String checkContents = Builder.settings.get(this.projectName, String.class);
+
+ return hashedContents != null && hashedContents.equals(checkContents);
+ }
+
+ /**
+ * Saves the checksums for a given path
+ */
+ public
+ void saveChecksums() throws IOException {
+ // hash/save the sources *and check-summed files* files
+ String hashedContents = generateChecksums(this.checksumPaths);
+ Builder.settings.save(this.projectName, hashedContents);
+ }
+
+
+
+
+
+
+
+ /**
+ * Generates checksums for the given path
+ */
+ public static
+ String generateChecksum(File file) throws IOException {
+ synchronized (Project.class) {
+ // calculate the hash of file
+ boolean found = false;
+ if (file.isFile() && file.canRead()) {
+ found = true;
+ }
+
+ if (!found) {
+ return null;
+ }
+
+ MessageDigest sha1 = digestThreadLocal.get();
+ sha1.reset();
+
+ FileInputStream inputStream = null;
+ try {
+ inputStream = new FileInputStream(file);
+ FileChannel channel = inputStream.getChannel();
+
+ long length = file.length();
+ if (length > Integer.MAX_VALUE) {
+ // you could make this work with some care,
+ // but this code does not bother.
+ throw new IOException("File " + file.getAbsolutePath() + " is too large.");
+ }
+
+ ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
+
+ int bufsize = 1024 * 8;
+ byte[] temp = new byte[bufsize];
+ int bytesRead = 0;
+
+ while (bytesRead < length) {
+ int numBytes = (int) length - bytesRead >= bufsize ? bufsize : (int) length - bytesRead;
+ buffer.get(temp, 0, numBytes);
+ sha1.update(temp, 0, numBytes);
+ bytesRead += numBytes;
+ }
+
+ byte[] hashBytes = sha1.digest();
+ return Base64Fast.encodeToString(hashBytes, false);
+ } finally {
+ IO.closeQuietly(inputStream);
+ }
+ }
+ }
+
+ /**
+ * Generates checksums for the given path
+ */
+ public static
+ String generateChecksums(Paths... paths) throws IOException {
+ synchronized (Project.class) {
+ // calculate the hash of all the files in the source path
+ Set names = new HashSet(64);
+
+ for (Paths path : paths) {
+ names.addAll(path.getPaths());
+ }
+
+ // hash of all files. faster than using java to hash files
+ MessageDigest sha1 = digestThreadLocal.get();
+ sha1.reset();
+
+ int bufsize = 1024 * 8;
+ byte[] temp = new byte[bufsize];
+ int bytesRead = 0;
+
+ boolean found = false;
+ for (String name : names) {
+ File file = new File(name);
+ if (file.isFile() && file.canRead()) {
+ found = true;
+
+ FileInputStream inputStream = null;
+ try {
+ inputStream = new FileInputStream(file);
+ FileChannel channel = inputStream.getChannel();
+
+ long length = file.length();
+ if (length > Integer.MAX_VALUE) {
+ // you could make this work with some care,
+ // but this code does not bother.
+ throw new IOException("File " + file.getAbsolutePath() + " is too large.");
+ }
+
+ ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
+
+ while (bytesRead < length) {
+ int numBytes = (int) length - bytesRead >= bufsize ? bufsize : (int) length - bytesRead;
+ buffer.get(temp, 0, numBytes);
+ sha1.update(temp, 0, numBytes);
+ bytesRead += numBytes;
+ }
+ } finally {
+ IO.closeQuietly(inputStream);
+ }
+ }
+ }
+
+ if (!found) {
+ return null;
+ }
+
+ byte[] hashBytes = sha1.digest();
+ return Base64Fast.encodeToString(hashBytes, false);
+ }
+ }
+}
diff --git a/src/dorkbox/build/util/ShutdownHook.java b/src/dorkbox/build/util/ShutdownHook.java
index 053a5ff..ed2b801 100644
--- a/src/dorkbox/build/util/ShutdownHook.java
+++ b/src/dorkbox/build/util/ShutdownHook.java
@@ -15,12 +15,12 @@
*/
package dorkbox.build.util;
-import com.esotericsoftware.wildcard.Paths;
-import dorkbox.Builder;
-import dorkbox.build.Project;
-
import java.io.IOException;
+import com.esotericsoftware.wildcard.Paths;
+
+import dorkbox.Builder;
+
public
class ShutdownHook implements Runnable {
private final Paths paths;
@@ -36,7 +36,7 @@ class ShutdownHook implements Runnable {
try {
BuildLog.start();
BuildLog.println("Saving build file checksums.");
- String hashedContents = Project.generateChecksums(paths);
+ String hashedContents = Hash.generateChecksums(paths);
Builder.settings.save("BUILD", hashedContents);
BuildLog.finish();
} catch (IOException e) {