Skip to content

Commit

Permalink
[JENKINS-43102] Add a tentative support for ARM CPUs (armv7l, armv6l,…
Browse files Browse the repository at this point in the history
… arm64)
  • Loading branch information
nfalco79 committed Mar 29, 2017
1 parent d7b685e commit ba9eb55
Show file tree
Hide file tree
Showing 5 changed files with 664 additions and 376 deletions.
64 changes: 60 additions & 4 deletions src/main/java/jenkins/plugins/nodejs/tools/CPU.java
@@ -1,19 +1,31 @@
package jenkins.plugins.nodejs.tools;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.commons.io.output.NullOutputStream;

import hudson.FilePath;
import hudson.Launcher;
import hudson.Proc;
import hudson.model.Computer;
import hudson.model.Node;
import hudson.remoting.VirtualChannel;
import hudson.util.StreamTaskListener;
import jenkins.MasterToSlaveFileCallable;

/**
* CPU type.
*/
public enum CPU {
i386, amd64;
i386, amd64, armv7l, armv6l, arm64;

/**
* Determines the CPU of the given node.
Expand All @@ -29,7 +41,7 @@ public static CPU of(@Nonnull Node node) throws IOException, InterruptedExceptio
if (computer == null) {
throw new DetectionFailedException("Node offline");
}
return detect(computer.getSystemProperties());
return detect(computer, computer.getSystemProperties());
}

/**
Expand All @@ -40,18 +52,62 @@ public static CPU of(@Nonnull Node node) throws IOException, InterruptedExceptio
* when the current platform node is not supported.
*/
public static CPU current() throws DetectionFailedException {
return detect(System.getProperties());
return detect(null, System.getProperties());
}

private static CPU detect(Map<Object, Object> systemProperties) throws DetectionFailedException {
private static CPU detect(@Nullable Computer computer, Map<Object, Object> systemProperties) throws DetectionFailedException {
String arch = ((String) systemProperties.get("os.arch")).toLowerCase(Locale.ENGLISH);
if (arch.contains("amd64") || arch.contains("86_64")) {
return amd64;
}
if (arch.contains("86")) {
return i386;
}
if (arch.contains("arm")) {
// try to get the specific architecture of arm CPU
try {
FilePath rootPath = new FilePath((computer != null ? computer.getChannel() : null), "/");
arch = rootPath.act(new ArchitectureCallable());
} catch (IOException | InterruptedException e) {
throw new DetectionFailedException("Unknown CPU architecture: " + arch, e);
}
switch (arch) {
case "armv7l":
return armv7l;
case "armv6l":
return armv6l;
case "arm64":
return arm64;
}
}
throw new DetectionFailedException("Unknown CPU architecture: " + arch);
}

/**
* Returns the machine hardware name for the current Linux computer.
*
* @author Nikolas Falco
*/
/* package */static class ArchitectureCallable extends MasterToSlaveFileCallable<String> {
private static final long serialVersionUID = 1L;

@Override
public String invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
Charset charset = Charset.defaultCharset();

FilePath basePath = new FilePath(f);
Launcher launcher = basePath.createLauncher(new StreamTaskListener(new NullOutputStream(), charset));

ByteArrayOutputStream baos = new ByteArrayOutputStream();

Proc starter = launcher.launch().cmdAsSingleString("uname -m").stdout(baos).start();
int exitCode = starter.join();
if (exitCode != 0) {
throw new IOException("Fail to execute 'uname -m' because: " + baos.toString(charset.name()));
}

return new String(baos.toByteArray(), charset).trim();
}
};

}
Expand Up @@ -11,7 +11,7 @@
/**
* Calculate the name of the installer for the specified version according the
* architecture and CPU of the destination node.
*
*
* @author fcamblor
* @author Nikolas Falco
*/
Expand All @@ -22,7 +22,7 @@ public class LatestInstallerPathResolver implements InstallerPathResolver {

private static final NodeJSVersionRange[] MSI_RANGES = new NodeJSVersionRange[] { new NodeJSVersionRange("[0, 4.5)"),
new NodeJSVersionRange("[5, 6.2]") };

/*
* (non-Javadoc)
* @see jenkins.plugins.nodejs.tools.InstallerPathResolver#resolvePathFor(java.lang.String, jenkins.plugins.nodejs.tools.Platform, jenkins.plugins.nodejs.tools.CPU)
Expand Down Expand Up @@ -70,6 +70,14 @@ public String resolvePathFor(String version, Platform platform, CPU cpu) {
}
arch = "x64";
break;
case arm64:
case armv6l:
case armv7l:
if (NodeJSVersion.parseVersion(version).compareTo(new NodeJSVersion(4, 0, 0)) < 0) {
throw new IllegalArgumentException("Unresolvable nodeJS installer for version=" + version + ", cpu=" + cpu.name() + ", platform=" + platform.name());
}
arch = cpu.name();
break;
default:
throw new IllegalArgumentException("Unresolvable nodeJS installer for version=" + version + ", cpu=" + cpu.name());
}
Expand Down
@@ -0,0 +1,22 @@
package jenkins.plugins.nodejs.tools;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;

import java.io.File;

import org.junit.Test;

public class ArchitectureCallableTest {

@Test
public void test_uname_on_linux() throws Exception {
assumeThat(Platform.current(), isOneOf(Platform.LINUX, Platform.OSX));

CPU.ArchitectureCallable callable = new CPU.ArchitectureCallable();
String machine = callable.invoke(new File("/"), null);
assertThat(machine, anyOf(containsString("86"), containsString("64"), containsString("arm")));
}

}
Expand Up @@ -64,6 +64,10 @@ public static Collection<Object[]> data() throws Exception {

for (Platform platform : Platform.values()) {
for (CPU cpu : CPU.values()) {
if (cpu.name().startsWith("arm") && platform != Platform.LINUX) {
// arm are only supported on linux
continue;
}
String testName = String.format("version=%s,cpu=%s,platform=%s", installable.id, cpu.name(), platform.name());
testPossibleParams.add(new Object[] { installable, platform, cpu, testName });
}
Expand Down

0 comments on commit ba9eb55

Please sign in to comment.