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

Use only cloc.pl on both Unix and Windows #43

Merged
merged 10 commits into from
Jan 9, 2025
Merged
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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
steps:
- name: Checkout latest
uses: actions/checkout@v4
- name: Set up Perl
uses: shogo82148/actions-setup-perl@v1.31.4
- name: Set up JDK 8
uses: actions/setup-java@v4
with:
Expand All @@ -55,6 +57,8 @@ jobs:
steps:
- name: Checkout latest
uses: actions/checkout@v4
- name: Set up Perl
uses: shogo82148/actions-setup-perl@v1.31.4
- name: Set up JDK 8
uses: actions/setup-java@v4
with:
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ following dependency in your Maven project:
## Requirements

This library requires a minimum of Java 8. You don't need to have `cloc` installed on your system, as the JAR comes
bundled with the necessary script. Since it is written in Perl, it should be executable out of the box on most systems
(as the majority of Unix-like systems have it installed by default).
bundled with the necessary script.

> [!NOTE]
> Since this script is written in Perl, you must make sure that its interpreter is installed and located on your `PATH`.
> Should work out of the box on most systems (as the majority of Unix-like systems have it installed by default),
> but worth pointing out in case you plan on using this on a minimalistic Linux image or Windows.

## Example

Expand Down
8 changes: 6 additions & 2 deletions doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ following dependency in your Maven project:
## Requirements

This library requires a minimum of Java 8. You don't need to have `cloc` installed on your system, as the JAR comes
bundled with the necessary script. Since it is written in Perl, it should be executable out of the box on most systems
(as the majority of Unix-like systems have it installed by default).
bundled with the necessary script.

> [!NOTE]
> Since this script is written in Perl, you must make sure that its interpreter is installed and located on your `PATH`.
> Should work out of the box on most systems (as the majority of Unix-like systems have it installed by default),
> but worth pointing out in case you plan on using this on a minimalistic Linux image or Windows.

## Example

Expand Down
15 changes: 2 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<java.version>8</java.version>
<cloc.version>2.02</cloc.version>
<cloc.url>https://github.com/AlDanial/cloc</cloc.url>
<cloc.md5>31533dd445610364154ec328bfcf7b2c</cloc.md5>
<org.apache.commons-lang.version>3.17.0</org.apache.commons-lang.version>
<org.codehaus.plexus.version>4.0.2</org.codehaus.plexus.version>
<org.jetbrains.annotations.version>26.0.1</org.jetbrains.annotations.version>
Expand Down Expand Up @@ -297,19 +298,7 @@
<configuration>
<url>${cloc.url}/releases/download/v${cloc.version}/cloc-${cloc.version}.pl</url>
<outputFileName>cloc.pl</outputFileName>
<md5>31533dd445610364154ec328bfcf7b2c</md5>
</configuration>
</execution>
<execution>
<id>default-download-exe</id>
<phase>initialize</phase>
<goals>
<goal>wget</goal>
</goals>
<configuration>
<url>${cloc.url}/releases/download/v${cloc.version}/cloc-${cloc.version}.exe</url>
<outputFileName>cloc.exe</outputFileName>
<md5>31adac93c8d3c9e4d67912a8f9f5b729</md5>
<md5>${cloc.md5}</md5>
</configuration>
</execution>
</executions>
Expand Down
86 changes: 53 additions & 33 deletions src/main/java/ch/usi/si/seart/cloc/CLOC.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
Expand All @@ -36,10 +37,6 @@
*/
public final class CLOC {

private static final String CMD = "cloc";

private static final String EXECUTABLE = getBundledExecutable();

private static final JsonMapper DEFAULT_MAPPER = new JsonMapper();
private static volatile JsonMapper OUTPUT_MAPPER = DEFAULT_MAPPER;

Expand Down Expand Up @@ -67,6 +64,46 @@ private static Properties getProperties() {
return PROPERTIES;
}

private static volatile File EXECUTABLE;
private static File getExecutable() {
if (EXECUTABLE == null) {
synchronized (CLOC.class) {
if (EXECUTABLE == null) {
URL url = CLOC.class.getClassLoader().getResource("cloc.pl");
String protocol = Objects.requireNonNull(url).getProtocol();
switch (protocol) {
case "file":
String path = url.getPath();
EXECUTABLE = new File(path);
break;
case "jar":
try {
File script = new File(SystemUtils.USER_HOME, "cloc.pl");
if (getMD5() != null && script.exists() && MD5.hash(script).equals(getMD5())) {
EXECUTABLE = script;
break;
}
FileUtils.copyURLToFile(url, script);
boolean success = script.setExecutable(true);
if (success) {
EXECUTABLE = script;
break;
}
throw new IOException("Unable change execute permissions: " + script);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException(ex);
}
default:
throw new UnsupportedOperationException("Unsupported protocol: " + protocol);
}
}
}
}
return EXECUTABLE;
}

/**
* @return the {@code cloc} repository link, or {@code null} if the details could not be loaded.
*/
Expand All @@ -83,6 +120,14 @@ public static String getVersion() {
return getProperties().getProperty("cloc.version");
}

/**
* @return the {@code cloc} executable MD5 checksum, or {@code null} if the details could not be loaded.
*/
@Nullable
public static String getMD5() {
return getProperties().getProperty("cloc.md5");
}

/**
* Set the {@link JsonMapper} to use for parsing the output of the command.
*
Expand All @@ -101,32 +146,6 @@ public static Builder command() {
return new Builder();
}

private static String getBundledExecutable() {
String extension = SystemUtils.IS_OS_WINDOWS ? "exe" : "pl";
URL url = CLOC.class.getClassLoader().getResource(CMD + "." + extension);
String protocol = Objects.requireNonNull(url).getProtocol();
switch (protocol) {
case "file":
return new File(url.getFile()).getPath();
case "jar":
try {
File script = new File(SystemUtils.JAVA_IO_TMPDIR, CMD);
FileUtils.copyURLToFile(url, script);
boolean success = script.setExecutable(true);
if (success) {
script.deleteOnExit();
return script.getAbsolutePath();
} else {
throw new IOException("Unable change execute permissions: " + script.getAbsolutePath());
}
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
default:
throw new UnsupportedOperationException("Unsupported protocol: " + protocol);
}
}

/**
* Facilitates the construction of {@link CLOC} command instances.
* It allows for the step-by-step creation of these objects
Expand Down Expand Up @@ -270,12 +289,13 @@ public Builder skipUniqueness(boolean value) {
*/
@Contract("_ -> new")
public @NotNull CLOC target(@NotNull Path path) {
Objects.requireNonNull(path, "Path must not be null!");
File file = path.toFile();
File executable = getExecutable();
File file = Objects.requireNonNull(path, "Path must not be null!").toFile();
if (!file.exists()) throw new IllegalArgumentException("Unable to read: " + path);

CommandLine commandLine = new CommandLine();
commandLine.setExecutable(EXECUTABLE);
commandLine.createArg("perl");
commandLine.createArg().setFile(executable);
commandLine.createArg().setFile(file);
flags.stream()
.map(flag -> "--" + flag)
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/ch/usi/si/seart/cloc/MD5.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ch.usi.si.seart.cloc;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

class MD5 {

private MD5() {
}

@SuppressWarnings({"checkstyle:EmptyStatement", "StatementWithEmptyBody"})
static String hash(File file) throws IOException, NoSuchAlgorithmException {
try (
InputStream fileStream = Files.newInputStream(file.toPath());
DigestInputStream digestStream = new DigestInputStream(fileStream, MessageDigest.getInstance("MD5"))
) {
byte[] buffer = new byte[8192];
while (digestStream.read(buffer, 0, buffer.length) != -1);
byte[] digested = digestStream.getMessageDigest().digest();
return new BigInteger(1, digested).toString(16);
}
}
}
1 change: 1 addition & 0 deletions src/main/resources/cloc.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
cloc.url=${cloc.url}
cloc.version=${cloc.version}
cloc.md5=${cloc.md5}
7 changes: 7 additions & 0 deletions src/test/java/ch/usi/si/seart/cloc/CLOCTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,11 @@ void testGetVersion() {
Assertions.assertNotNull(version, "Version should not be null.");
Assertions.assertFalse(version.isEmpty(), "Version should not be empty.");
}

@Test
void testGetMD5() {
String md5 = CLOC.getMD5();
Assertions.assertNotNull(md5, "MD5 should not be null.");
Assertions.assertFalse(md5.isEmpty(), "MD5 should not be empty.");
}
}
Loading