package project.combiner;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import project.Feature;
import project.Plugin;
import project.Snapshot;
import utils.lists.ArrayList;
import utils.lists.Arrays;
import utils.lists.Files;
import utils.lists.HashMap;
import utils.lists.List;
import utils.lists.Map;
import utils.lists.Pair;
import utils.lists.Paths;
import utils.lists.PosixFilePermissions;
import utils.streams.functions.ExDoubleConsumer;
import utils.streams2.IOStream;
public class MainBuildIDE {
private static final String DATE_PATTERN_LONG = "EEE MMM d HH':'mm':'ss zzz uuuu";
private static final String DATE_PATTERN_SHORT = "yyyyMMddHHmm";
private static final String ECLIPSE_INI = "eclipse.ini";
private static final String MACOS_ECLIPSE_INI = "Eclipse.app/Contents/MacOS/eclipse.ini";
private static final String CONFIG_INI = "configuration/config.ini";
private static final String BUNDLES_INFO = "configuration/org.eclipse.equinox.simpleconfigurator/bundles.info";
private static final String PLATFORM_XML = "configuration/org.eclipse.update/platform.xml";
private static final String SOURCE_INFO = "configuration/org.eclipse.equinox.source/source.info";
private static final String ORG_ECLIPSE_UI_IDE_PREFS = "configuration/.settings/org.eclipse.ui.ide.prefs";
private static final String ARTIFACTS_XML = "artifacts.xml";
private static final String DOT_ECLIPSEPRODUCT = ".eclipseproduct";
private static final String JVMARGS =
"p2/org.eclipse.equinox.p2.engine/profileRegistry/ECLIPSE_PROFILE_ID.profile/.data/org.eclipse.equinox.internal.p2.touchpoint.eclipse.actions/jvmargs";
private static final String PROFILE_GZ =
"p2/org.eclipse.equinox.p2.engine/profileRegistry/ECLIPSE_PROFILE_ID.profile/PROFILE_TIME.profile.gz/PROFILE_TIME.profile";
private static final String ECLIPSE_PRODUCT_ID = "org.eclipse.sdk.ide";
private static final String ECLIPSE_PROFILE_ID = "SDKProfile";
private static final Locale ENGLISH = Locale.ENGLISH;
private static final Charset UTF8 = StandardCharsets.UTF_8;
private static final DateTimeFormatter DATE_SHORT = DateTimeFormatter.ofPattern(DATE_PATTERN_SHORT, ENGLISH);
private static final DateTimeFormatter DATE_LONG = DateTimeFormatter.ofPattern(DATE_PATTERN_LONG, ENGLISH);
private static final ZonedDateTime NOW = ZonedDateTime.now(ZoneId.of("UTC"));
private static final Map<String, String> ENV = environment();
private static final String FILES = ENV.getOrDefault("files", "");
private static final String BUILD_TYPE = ENV.getOrDefault("build", "test");
private static final String RUNNING = ENV.getOrDefault("running", "");
private static final String WORKSPACE = ENV.getOrDefault("workspace", "");
private static final String OSGI_OS = ENV.getOrDefault("osgios", "win32");
private static final String OSGI_WS = ENV.getOrDefault("osgiws", "win32");
private static final Path CURRENT_FOLDER = Paths.get("").toAbsolutePath();
private static final Path ROOT = CURRENT_FOLDER.getParent().getParent();
private static final Path IDE_ZIP = CURRENT_FOLDER.resolve("target/ide.zip");
private static final Path TARGET_IDE1 = CURRENT_FOLDER.resolve("target/ide-1");
private static final Path TARGET_IDE2 = CURRENT_FOLDER.resolve("target/ide-2");
private static final Path TARGET_IDE3 = CURRENT_FOLDER.resolve("target/ide-99");
private static final Path TARGET_IDE = determineUnusedTarget();
private static Snapshot snapshot = new Snapshot(NOW.toInstant()).addPathRemapper(MainBuildIDE::remapP2);
private static long timeMillis;
private static String oldPhase;
private static int phaseCounter;
private static boolean fullBuild;
private static Instant pluginsModified = NOW.toInstant();
private static Instant featuresModified = NOW.toInstant();
private static Instant p2Modified = NOW.toInstant();
private static Instant platformModified = NOW.toInstant();
private static Instant sourceModified = NOW.toInstant();
private static Instant launcherModified = NOW.toInstant();
private static String PROFILE_TIME = String.valueOf(platformModified.toEpochMilli());
private static HashMap<Path, HashMap<String, String>> pluginToManifest;
public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
fullBuild = "test".equals(BUILD_TYPE) || "full".equals(BUILD_TYPE);
System.out.print("IDE " + BUILD_TYPE + " build, ");
ArrayList<Path> changes = reportedChanges();
System.out.println(changes.size() + " file(s) to go, target is " + TARGET_IDE);
if(changes.isEmpty()) {
return;
}
String workspace = "".equals(WORKSPACE) ? null : new File(new URI(WORKSPACE)).toPath().toString();
updateTime();
mergeChanges(changes);
updateFiles(workspace);
writeSnapshot();
writeIDE();
}
static void initializeFromSnapshot() throws IOException {
readSnapshot();
updateFiles(null);
}
private static void updateFiles(String workspace) throws IOException {
try {
snapshot.copyFiles(
"plugins/org.eclipse.m2e.maven.indexer/indexer-core-3.1.0.jar/org/apache/maven/index",
"plugins/org.eclipse.m2e.maven.indexer/org/apache/maven/index");
replacePlaceholders();
pluginToManifest = getPluginToManifest();
if(workspace != null && isRunningSelfHosted()) {
registerFile(ORG_ECLIPSE_UI_IDE_PREFS, orgEclipseUiIdePrefs(workspace), p2Modified);
} else {
snapshot.removeFile(Paths.get(ORG_ECLIPSE_UI_IDE_PREFS));
}
registerFile(JVMARGS, jvmargs(), p2Modified);
registerFile(BUNDLES_INFO, bundlesInfo(), pluginsModified);
registerFile(CONFIG_INI, configIni(), platformModified);
registerFile(ECLIPSE_INI, eclipseIni(false), launcherModified);
registerFile(MACOS_ECLIPSE_INI, eclipseIni(true), launcherModified);
registerFile(PLATFORM_XML, platformXml(), featuresModified);
registerFile(SOURCE_INFO, sourceInfo(), sourceModified);
registerFile(ARTIFACTS_XML, artifactsXml(), p2Modified);
registerFile(DOT_ECLIPSEPRODUCT, dotEclipseproduct(), platformModified);
registerFile(
PROFILE_GZ,
P2ProfileGenerator.profileGz(snapshot, PROFILE_TIME, ECLIPSE_PROFILE_ID, ECLIPSE_PRODUCT_ID),
platformModified);
} catch(RuntimeException e) {
System.out.println("\nProbably transient error generating dynamic files: " + e.getMessage());
e.printStackTrace(System.out);
}
}
private static byte[] orgEclipseUiIdePrefs(String workspace) {
ArrayList<String> lines = new ArrayList<>();
String workspacePath = Paths.get(workspace).toString().replace("\\", "\\\\").replace(":", "\\:");
lines.add("MAX_RECENT_WORKSPACES=5");
lines.add("RECENT_WORKSPACES=" + workspacePath);
lines.add("RECENT_WORKSPACES_PROTOCOL=3");
lines.add("SHOW_WORKSPACE_SELECTION_DIALOG=false");
lines.add("eclipse.preferences.version=1");
lines.add("");
String orgEclipseUiIdePrefs = String.join("\n", lines);
return orgEclipseUiIdePrefs.getBytes(UTF8);
}
private static byte[] dotEclipseproduct() {
String buildID = getBundleVersion("org.eclipse.platform").replace(".qualifier", "");
String dotEclipseproduct =
"name=Eclipse Platform\n" + "id=org.eclipse.platform\n" + "version=" + buildID + "\n" + "";
return dotEclipseproduct.getBytes(UTF8);
}
private static byte[] artifactsXml() {
String artifactsXml =
"<?xml version='1.0' encoding='UTF-8'?>\n" +
"<?artifactRepository version='1.1.0'?>\n" +
"<repository name='Bundle pool' type='org.eclipse.equinox.p2.artifact.repository.simpleRepository' version='1'>\n" +
" <properties size='2'>\n" +
" <property name='p2.system' value='true'/>\n" +
" <property name='p2.timestamp' value='" +
p2Modified.toEpochMilli() +
"'/>\n" +
" </properties>\n" +
" <mappings size='3'>\n" +
" <rule filter='(& (classifier=osgi.bundle))' output='${repoUrl}/plugins/${id}_${version}.jar'/>\n" +
" <rule filter='(& (classifier=binary))' output='${repoUrl}/binary/${id}_${version}'/>\n" +
" <rule filter='(& (classifier=org.eclipse.update.feature))' output='${repoUrl}/features/${id}_${version}.jar'/>\n" +
" </mappings>\n" +
" <artifacts size='0'>\n" +
" </artifacts>\n" +
"</repository>\n" +
"";
return artifactsXml.getBytes(UTF8);
}
private static byte[] sourceInfo() {
ArrayList<String> lines = new ArrayList<>();
lines.addAll("#encoding=UTF-8", "#version=1");
List<Path> all = snapshot.listAll(Paths.get("plugins")).sort();
for(Path path : all) {
String sourceBundle = path.getFileName().toString();
if(sourceBundle.contains(".source_")) {
int index = sourceBundle.indexOf(".source_") + 7;
String name = sourceBundle.substring(0, index);
String suffix = sourceBundle.endsWith(".jar") ? ".jar" : "/";
int endIndex = sourceBundle.length() - (suffix == "/" ? 0 : 4);
String version = sourceBundle.substring(index + 1, endIndex);
String line = name + "," + version + ",plugins/" + name + "_" + version + suffix + ",-1,false";
lines.add(line);
}
}
lines.add("");
String sourceInfo = String.join("\n", lines);
return sourceInfo.getBytes(UTF8);
}
private static byte[] platformXml() throws IOException {
ArrayList<String> lines = new ArrayList<>();
lines.add("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
lines.add("<config transient=\"false\" date=\"" + featuresModified.toEpochMilli() + "\">");
lines.add("\t<site enabled=\"true\" updateable=\"true\" policy=\"USER-EXCLUDE\" url=\"platform:/base/\">");
for(Path path : snapshot.listFolders(Paths.get("features"))) {
Feature feature = snapshot.getFileAsFeature(path);
if(feature != null) {
addFeatureLine(lines, feature.id, feature.version, "/");
}
}
lines.addAll("\t</site>", "</config>", "");
String platformXml = String.join("\n", lines);
return platformXml.getBytes(UTF8);
}
private static void addFeatureLine(ArrayList<String> lines, String id, String version, String suffix) {
lines.add(String.format(
"\t\t<feature id=\"%1$s\" url=\"features/%1$s_%2$s%3$s\" version=\"%2$s\">",
id,
version,
suffix));
lines.add("\t\t</feature>");
}
private static byte[] eclipseIni(boolean insideEclipseApp) {
String launcher = getBundleFilename("org.eclipse.equinox.launcher");
String launcherName = "org.eclipse.equinox.launcher." + OSGI_OS + "." + OSGI_WS + ".x86_64";
String launcherLibrary = getBundleFilename(launcherName);
ArrayList<String> lines = new ArrayList<>();
Path jvmDLLPath = Paths.get(System.getProperty("java.home", "")).resolve("bin/server/jvm.dll");
String pluginsPath = insideEclipseApp ? "../../../plugins/" : "plugins/";
if(Files.isRegularFile(jvmDLLPath)) {
String jvmDLL = jvmDLLPath.toString();
lines.addAll("-vm", jvmDLL);
}
lines.add("-startup");
lines.add(pluginsPath + launcher);
lines.add("--launcher.library");
lines.add(pluginsPath + launcherLibrary);
lines.add("-product");
lines.add(ECLIPSE_PRODUCT_ID);
lines.add("--launcher.defaultAction");
lines.add("openFile");
lines.add("-showsplash");
lines.add("org.eclipse.platform");
lines.add("--launcher.defaultAction");
lines.add("openFile");
lines.add("--launcher.appendVmargs");
lines.add("-vmargs");
lines.add("-Dosgi.requiredJavaVersion=1.7");
lines.add("-Xms40m");
lines.add("-Xmx3500m");
if("macosx".equals(OSGI_OS)) {
if(insideEclipseApp) {
lines.add("-Xdock:icon=../Resources/Eclipse.icns");
} else {
lines.add("-Xdock:icon=Eclipse.app/Contents/Resources/Eclipse.icns");
}
lines.add("-XstartOnFirstThread");
lines.add("-Dorg.eclipse.swt.internal.carbon.smallFonts");
}
lines.add("");
String eclipseIni = String.join(System.lineSeparator(), lines);
return eclipseIni.getBytes(UTF8);
}
private static byte[] configIni() {
ZonedDateTime time = ZonedDateTime.ofInstant(platformModified, ZoneId.of("UTC"));
String date = DATE_LONG.format(time);
String osgi = getBundleFilename("org.eclipse.osgi");
String timestamp = DATE_SHORT.format(time);
String buildID = getBundleVersion("org.eclipse.platform").replace("qualifier", timestamp);
String simpleConfigurator = getBundleFilename("org.eclipse.equinox.simpleconfigurator");
String compatibilityState = getBundleFilename("org.eclipse.osgi.compatibility.state");
String documentsPath = "linux".equals(OSGI_OS) ? "" : "/Documents";
ArrayList<String> lines = new ArrayList<>();
lines.add("#This configuration file was written by: org.eclipse.equinox.internal.frameworkadmin.equinox.EquinoxFwConfigFileParser");
lines.add("#" + date);
lines.add("org.eclipse.update.reconcile=false");
lines.add("eclipse.p2.profile=" + ECLIPSE_PROFILE_ID);
lines.add("osgi.instance.area.default=@user.home" + documentsPath + "/workspace");
lines.add("osgi.framework=file\\:plugins/" + osgi);
lines.add("equinox.use.ds=true");
lines.add("eclipse.buildId=" + buildID);
lines.add("osgi.bundles=reference\\:file\\:" + simpleConfigurator + "@1\\:start");
lines.add("org.eclipse.equinox.simpleconfigurator.configUrl=file\\:org.eclipse.equinox.simpleconfigurator/bundles.info");
lines.add("eclipse.product=" + ECLIPSE_PRODUCT_ID);
lines.add("osgi.splashPath=platform\\:/base/plugins/org.eclipse.platform");
lines.add("osgi.framework.extensions=reference\\:file\\:" + compatibilityState);
lines.add("eclipse.application=org.eclipse.ui.ide.workbench");
lines.add("eclipse.p2.data.area=@config.dir/../p2");
lines.add("osgi.bundles.defaultStartLevel=4");
String configIni = String.join(System.lineSeparator(), lines);
return configIni.getBytes(UTF8);
}
private static String getBundleVersion(String name) {
Path fromZipPath1 = fromZipPath("plugins/" + name + ".jar");
HashMap<String, String> manifest = pluginToManifest.get(fromZipPath1);
if(manifest == null) {
Path fromZipPath2 = fromZipPath("plugins/" + name);
manifest = pluginToManifest.get(fromZipPath2);
if(manifest == null) {
throw new IllegalArgumentException("No manifest found for " + name);
}
}
String bundleVersion = "Bundle-Version";
String value = manifest.get(bundleVersion);
if(value == null) {
throw new IllegalArgumentException("Version not found from " + name);
}
return value;
}
private static String getBundleFilename(String name) {
Path path = fromZipPath("plugins/" + name + ".jar");
HashMap<String, String> manifest = pluginToManifest.get(path);
if(manifest != null) {
return generateBundleIDFromManifest(".jar", manifest);
}
path = fromZipPath("plugins/" + name);
manifest = pluginToManifest.get(path);
if(manifest == null) {
throw new IllegalArgumentException("No manifest found for " + name);
}
return generateBundleIDFromManifest("", manifest);
}
private static void registerFile(String name, byte[] bytes, Instant modified) throws IOException {
snapshot.addFile(Paths.get(name), bytes, modified);
}
private static byte[] jvmargs() {
ArrayList<String> lines = new ArrayList<>();
String date = DATE_LONG.format(ZonedDateTime.ofInstant(p2Modified, ZoneId.of("UTC")));
lines.add("#" + date);
if("macosx".equals(OSGI_OS)) {
lines.add("-Xms=40m,40m");
lines.add("-Xmx=512m,512m");
} else {
lines.add("-Xms=40m");
lines.add("-Xmx=512m");
}
lines.add("");
String bundlesInfo = String.join("\n", lines);
return bundlesInfo.getBytes(UTF8);
}
private static byte[] bundlesInfo() {
ArrayList<String> lines = new ArrayList<>();
lines.add("#version=1");
for(Path pluginPath : pluginToManifest.keySet()) {
String plugin = pluginPath.getFileName().toString();
String suffix = plugin.endsWith(".jar") ? ".jar" : "/";
HashMap<String, String> manifest = pluginToManifest.get(pluginPath);
addParsedBundleInfoFromManifest(suffix, lines, manifest);
}
lines.sort().add("");
String bundlesInfo = String.join("\n", lines);
return bundlesInfo.getBytes(UTF8);
}
private static void addParsedBundleInfoFromManifest(
String suffix,
ArrayList<String> lines,
HashMap<String, String> manifest) {
String bundleVersion = "Bundle-Version";
String version = manifest.get(bundleVersion);
String bundleSymbolicName = "Bundle-SymbolicName";
String symbolicName = manifest.get(bundleSymbolicName);
if(version != null && symbolicName != null) {
String name = symbolicName.split(";")[0].trim();
if(name.endsWith(".source") == false) {
String line = bundleInfoLine(name, version, suffix);
lines.add(line);
}
}
}
private static String bundleInfoLine(String name, String version, String ext) {
return name + "," + version + ",plugins/" + name + "_" + version + ext + "," + bundleStartSettings(name);
}
private static String bundleStartSettings(String plugin) {
switch(plugin) {/*Q*/
case "org.eclipse.core.runtime" : return "4,true" ;
case "org.eclipse.equinox.common" : return "2,true" ;
case "org.eclipse.equinox.ds" : return "2,true" ;
case "org.eclipse.equinox.event" : return "2,true" ;
case "org.eclipse.equinox.p2.reconciler.dropins": return "4,true" ;
case "org.eclipse.equinox.simpleconfigurator" : return "1,true" ;
case "org.eclipse.osgi" : return "-1,true";
default : return "4,false";
/*E*/}
}
private static ArrayList<Path> reportedChanges() throws IOException {
if(fullBuild) {
return streamAllFiles(ROOT);
}
if(FILES.isEmpty()) {
return ArrayList.of();
}
readSnapshot();
Path links = CURRENT_FOLDER.resolve("links");
Path projects = ROOT.resolve("projects");
ArrayList<String> list = ArrayList.of(FILES.replaceAll("^\"|\"$", "").split("\" \"")).removeIf(String::isEmpty);
ArrayList<Path> list2 = list.map(Paths::get);
list2.replaceAll(p -> replaceTrackingPathsWithReal(links, projects, p));
list2.filter(p -> p.startsWith(ROOT));
if(list2.size() == 1) {
Path path = list2.get(0);
if(path.startsWith(projects)) {
Path relativePath = ROOT.relativize(path);
if(relativePath.getNameCount() > 1 && isWatchedFile(relativePath) == false) {
Path folder = projects.resolve(relativePath.getName(1));
ArrayList<Path> list3 = Files.walk(folder).toList();
list3.filter(p -> Files.isRegularFile(p) && isWatchedFile(ROOT.relativize(p)));
return list3;
}
}
}
return list2;
}
private static Path replaceTrackingPathsWithReal(Path links, Path projects, Path p) {
if(p.startsWith(links)) {
return projects.resolve(links.relativize(p));
}
return p;
}
private static void replacePlaceholders() throws IOException {
updateJetty();
updateM2e();
updatePomVersionToFile(
"libraries/apache.httpcore/pom.xml",
"plugins/org.apache.httpcomponents.httpcore.jar/META-INF/MANIFEST.MF");
updatePomVersionToFile(
"libraries/apache.httpclient/pom.xml",
"plugins/org.apache.httpcomponents.httpclient.jar/META-INF/MANIFEST.MF");
updatePomVersionToManifest(
"libraries/ow2.sat4j/org.sat4j.core/pom.xml",
"plugins/org.sat4j.core.jar/META-INF/MANIFEST.MF",
"9.9.9.token");
updatePomVersionToManifest(
"libraries/ow2.sat4j/org.sat4j.pb/pom.xml",
"plugins/org.sat4j.pb.jar/META-INF/MANIFEST.MF",
"9.9.9.token");
updateAntVersionToFile(
"libraries/apache.ant/STATUS",
"plugins/org.apache.ant/ant-antlr.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-apache-bcel.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-apache-bsf.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-apache-log4j.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-apache-oro.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-apache-regexp.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-apache-resolver.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-apache-xalan2.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-commons-logging.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-commons-net.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-jai.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-javamail.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-jdepend.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-jmf.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-jsch.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-junit.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-junit4.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-launcher.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-netrexx.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-swing.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant-testutil.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/ant.jar/META-INF/MANIFEST.MF",
"plugins/org.apache.ant/META-INF/MANIFEST.MF");
updatePomVersionToFile("libraries/ow2.sat4j/org.sat4j.core/pom.xml", "plugins/org.sat4j.core.jar/sat4j.version");
updatePomVersionToMaven(
"eclipse.webtools-common/plugins/org.eclipse.wst.common.frameworks/pom.xml",
"org.eclipse.wst.common.frameworks.jar",
"org.eclipse.webtools.common",
"org.eclipse.wst.common.frameworks");
updatePomVersionToMaven(
"eclipse.webtools-sourceediting/bundles/org.eclipse.wst.sse.ui/pom.xml",
"org.eclipse.wst.sse.ui.jar",
"org.eclipse.webtools.sourceediting",
"org.eclipse.wst.sse.ui");
updatePomVersionToMaven(
"eclipse.webtools-sourceediting/bundles/org.eclipse.wst.xml.core/pom.xml",
"org.eclipse.wst.xml.core.jar",
"org.eclipse.webtools.sourceediting",
"org.eclipse.wst.xml.core");
updatePomVersionToMaven(
"nodeclipse.editbox/pm.eclipse.editbox/pom.xml",
"pm.eclipse.editbox.jar",
"pm.eclipse.editbox",
"pm.eclipse.editbox");
updatePomVersionToMaven(
"eclipse.jgit/org.eclipse.jgit.ui/pom.xml",
"org.eclipse.jgit.ui.jar",
"org.eclipse.jgit",
"org.eclipse.jgit.ui");
updatePomVersionToMaven(
"nodeclipse.pluginslist/org.nodeclipse.pluginslist.core/pom.xml",
"org.nodeclipse.pluginslist.core.jar",
"org.nodeclipse.pluginslist",
"org.nodeclipse.pluginslist.core");
updatePomVersionToMaven(
"eclipse.e4.tools/bundles/org.eclipse.e4.tools.spy/pom.xml",
"org.eclipse.e4.tools.spy.jar",
"org.eclipse.e4",
"org.eclipse.e4.tools.spy");
updatePomVersionToMaven(
"eclipse.e4.tools/bundles/org.eclipse.e4.tools.css.spy/pom.xml",
"org.eclipse.e4.tools.css.spy.jar",
"org.eclipse.e4",
"org.eclipse.e4.tools.css.spy");
updatePomVersionToMaven(
"eclipse.egit.github/org.eclipse.mylyn.github.doc/pom.xml",
"org.eclipse.mylyn.github.doc.jar",
"org.eclipse.mylyn.github",
"org.eclipse.mylyn.github.doc");
updatePomVersionToMaven(
"eclipse.jgit/org.eclipse.jgit.pgm/pom.xml",
"org.eclipse.jgit.pgm.jar",
"org.eclipse.jgit",
"org.eclipse.jgit.pgm");
updatePomVersionToMaven(
"eclipse.egit/org.eclipse.egit.mylyn.ui/pom.xml",
"org.eclipse.egit.mylyn.ui.jar",
"org.eclipse.egit",
"org.eclipse.egit.mylyn.ui");
updatePomVersionToMaven(
"eclipse.egit.github/org.eclipse.egit.github.core/pom.xml",
"org.eclipse.egit.github.core.jar",
"org.eclipse.egit.github",
"org.eclipse.egit.github.core");
updatePomVersionToMaven(
"eclipse.egit.github/org.eclipse.mylyn.github.core/pom.xml",
"org.eclipse.mylyn.github.core.jar",
"org.eclipse.mylyn.github",
"org.eclipse.mylyn.github.core");
updatePomVersionToMaven(
"eclipse.gef/org.eclipse.draw2d/pom.xml",
"org.eclipse.draw2d.jar",
"org.eclipse.draw2d.plugins",
"org.eclipse.draw2d");
updatePomVersionToMaven(
"eclipse.egit.github/org.eclipse.mylyn.github.ui/pom.xml",
"org.eclipse.mylyn.github.ui.jar",
"org.eclipse.mylyn.github",
"org.eclipse.mylyn.github.ui");
}
private static void updateJetty() throws IOException {
String group = "org.eclipse.jetty";
String dir = "eclipse.jetty.project";
updatePomVersionToJetty(group, dir, "continuation");
updatePomVersionToJetty(group, dir, "security");
updatePomVersionToJetty(group, dir, "servlet");
updatePomVersionToJetty(group, dir, "server");
updatePomVersionToJetty(group, dir, "http");
updatePomVersionToJetty(group, dir, "util");
updatePomVersionToJetty(group, dir, "io");
}
private static void updatePomVersionToJetty(String group, String dir, String project) throws IOException {
String plugin = "jetty-" + project;
String pomPath = dir + "/" + plugin + "/pom.xml";
String pluginDir = "org.eclipse.jetty." + project + ".jar";
updatePomVersionToMaven(pomPath, pluginDir, group, plugin);
}
private static void updateM2e() throws IOException {
String dir1 = "eclipse.m2e.core";
String group1 = "org.eclipse.m2e";
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.core.ui");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.core");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.discovery");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.editor.xml");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.editor");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.jdt.ui");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.jdt");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.launching");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.lifecyclemapping.defaults");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.logback.appender");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.logback.configuration");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.model.edit");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.profiles.core");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.profiles.ui");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.refactoring");
undatePomVersionToM2e(dir1, group1, "org.eclipse.m2e.scm");
String dir2 = "eclipse.m2e.core/m2e-maven-runtime";
undatePomVersionToM2e(dir2, group1, "org.eclipse.m2e.maven.runtime");
undatePomVersionToM2e(dir2, group1, "org.eclipse.m2e.maven.runtime.slf4j.simple");
undatePomVersionToM2e(dir2, group1, "org.eclipse.m2e.archetype.common");
undatePomVersionToM2e(dir2, group1, "org.eclipse.m2e.maven.indexer");
String dir3 = "eclipse.m2e.workspace";
String group2 = "io.takari.m2e.workspace";
undatePomVersionToM2e(dir3, group2, "org.eclipse.m2e.workspace.cli");
}
private static void undatePomVersionToM2e(String dirName, String group, String plugin) throws IOException {
String pomPath = dirName + "/" + plugin + "/pom.xml";
updatePomVersionToMaven(pomPath, plugin, group, plugin);
}
private static void updatePomVersionToMaven(String pomPath, String pluginDir, String group, String plugin)
throws IOException {
String pom = "libraries/" + pomPath;
String manifest = "plugins/" + pluginDir + "/META-INF/MANIFEST.MF";
String pomProps = "plugins/" + pluginDir + "/META-INF/maven/" + group + "/" + plugin + "/pom.properties";
String pomXml = "plugins/" + pluginDir + "/META-INF/maven/" + group + "/" + plugin + "/pom.xml";
updatePomVersionToFile(pom, manifest, pomProps, pomXml);
}
private static void updateAntVersionToFile(String ant, String... files) throws IOException {
String fullVersion = readVersionFromFile(ant, "Development:", "(in GIT Branch: master)", "") + ".qualifier";
updateVersionToFile(fullVersion, files);
}
private static void updatePomVersionToFile(String pom, String... files) throws IOException {
String fullVersion = readVersionFromPom(pom).replace("SNAPSHOT", "qualifier");
updateVersionToFile(fullVersion, files);
}
private static void updateVersionToFile(String fullVersion, String... files) throws IOException {
int indexOf = fullVersion.indexOf('.');
String majorVersion = indexOf == -1 ? fullVersion : fullVersion.substring(0, indexOf);
indexOf = indexOf == -1 ? -1 : fullVersion.indexOf('.', indexOf + 1);
String minorVersion = indexOf == -1 ? fullVersion : fullVersion.substring(0, indexOf);
indexOf = indexOf == -1 ? -1 : fullVersion.indexOf('.', indexOf + 1);
String serviceVersion = indexOf == -1 ? fullVersion : fullVersion.substring(0, indexOf);
for(String file : files) {
Path filePath = fromZipPath(file);
findAndReplaceInTextFile(filePath, "99.99.99.99", fullVersion);
findAndReplaceInTextFile(filePath, "99.99.99", serviceVersion);
findAndReplaceInTextFile(filePath, "99.99", minorVersion);
findAndReplaceInTextFile(filePath, "99", majorVersion);
}
}
private static void updatePomVersionToManifest(String pom, String manifest, String token) throws IOException {
Path manifestPath = fromZipPath(manifest);
String versionFromPom = readVersionFromPom(pom);
findAndReplaceInTextFile(manifestPath, token, versionFromPom);
}
private static String readVersionFromPom(String pom) throws IOException {
return readVersionFromFile(pom, "<version>", "</version>", "SNAPSHOT");
}
private static String readVersionFromFile(String file, String openingTag, String closingTag, String marker)
throws IOException {
ArrayList<String> list =
Files.readAllLines(ROOT.resolve(file)).filter(s -> s.contains(openingTag) && s.contains(marker));
if(list.isEmpty()) {
return "9.9.9.token";
}
String version = list.get(-1).replace(openingTag, "").replace(closingTag, "").trim();
return version.replace('-', '.');
}
private static void findAndReplaceInTextFile(Path key, String target, String replacement) throws IOException {
String fileAsString = snapshot.getFileAsString(toZipPath(key));
if(fileAsString == null) {
return;
}
String replace = fileAsString.replace(target, replacement);
snapshot.replaceFile(toZipPath(key), replace);
}
private static void writeIDE() throws IOException, InterruptedException {
if(fullBuild && Files.isDirectory(TARGET_IDE)) {
FileTime epoch = FileTime.from(Instant.EPOCH);
Files.walk(TARGET_IDE).forEach(p -> Files.setLastModifiedTime(p, epoch));
}
snapshot.write(TARGET_IDE, monitor("target"));
changePermissions("Eclipse.app/Contents/MacOS/eclipse", "rwxr-x---");
changePermissions("eclipse", "rwxr-xr-x");
changePermissions("icon.xpm", "rwxr-xr-x");
}
private static void changePermissions(String other, String permissions) {
Path path = TARGET_IDE.resolve(other);
if(Files.isRegularFile(path)) {
try {
Files.setPosixFilePermissions(path, PosixFilePermissions.fromString(permissions));
} catch(@SuppressWarnings("unused") IOException | UnsupportedOperationException ignored) {}
}
}
private static Path remapP2(Path file) {
if(file.startsWith("p2/org.eclipse.equinox.p2.engine/profileRegistry") == false) {
return file;
}
ArrayList<Path> list = ArrayList.fromIterable(file);
if(list.size() > 3 && list.get(3).startsWith("ECLIPSE_PROFILE_ID.profile")) {
list.set(3, Paths.get(ECLIPSE_PROFILE_ID + ".profile"));
}
if(list.size() > 4 && list.get(4).startsWith("PROFILE_TIME.profile.gz")) {
list.set(4, Paths.get(PROFILE_TIME + ".profile.gz"));
}
if(list.size() > 5 && list.get(5).startsWith("PROFILE_TIME.profile")) {
list.set(5, Paths.get(PROFILE_TIME + ".profile"));
}
Path resolved = Paths.get("");
for(Path path : list) {
resolved = resolved.resolve(path);
}
return resolved;
}
private static String generateBundleIDFromManifest(String suffix, HashMap<String, String> manifest) {
String bundleVersion = "Bundle-Version";
String version = manifest.get(bundleVersion);
if(version == null) {
throw new IllegalArgumentException("Manifest does not have Bundle-Version");
}
String bundleSymbolicName = "Bundle-SymbolicName";
String symbolicName = manifest.get(bundleSymbolicName);
if(symbolicName == null) {
throw new IllegalArgumentException("Manifest does not have Bundle-SymbolicName");
}
String name = symbolicName.split(";")[0].trim();
return name + "_" + version + suffix;
}
static HashMap<Path, HashMap<String, String>> getPluginToManifest() throws IOException {
HashMap<Path, HashMap<String, String>> map = new HashMap<>();
for(Path pluginPath : snapshot.listAll(Paths.get("plugins"))) {
Plugin plugin = snapshot.getFileAsPlugin(pluginPath);
if(plugin != null &&
plugin.manifest.containsKey("Bundle-SymbolicName") &&
plugin.manifest.containsKey("Bundle-Version")) {
map.put(fromZipPath(pluginPath), plugin.manifest);
}
}
return map;
}
private static void writeSnapshot() throws IOException, InterruptedException {
if(snapshot.notFound(Paths.get("dropins"))) {
snapshot.addFolder(Paths.get("dropins"), NOW.toInstant());
}
snapshot.write(IDE_ZIP, monitor("cache"));
}
private static ExDoubleConsumer<InterruptedException> monitor(String phase) {
return d -> reportTime(phase, (int) (1000 * d), 1000);
}
private static void mergeChanges(ArrayList<Path> changes) throws IOException, InterruptedException {
long bytesRead = 0;
int total = changes.size();
int count = 0;
for(Path next : changes) {
reportTime("changes", ++count, total);
if(next.startsWith(ROOT) && Files.isDirectory(next) == false) {
Path path = ROOT.relativize(next);
if(isWatchedFile(path)) {
Path target = toCopyOperationTarget(path);
if(Files.isRegularFile(next)) {
Path updatedFile = ROOT.resolve(path);
Instant modified = Files.getLastModifiedTime(updatedFile).toInstant();
byte[] bytes = Files.readAllBytes(updatedFile);
bytesRead += bytes.length;
snapshot.addFile(toZipPath(target), bytes, modified);
} else {
snapshot.removeFile(toZipPath(target));
}
}
}
}
if(bytesRead > 10 * 1024 * 1024) {
System.out.printf("%n(done reading %,d bytes)", bytesRead);
}
}
private static void readSnapshot() {
if(Files.isRegularFile(IDE_ZIP)) {
try {
snapshot.read(IDE_ZIP);
} catch(IOException e) {
snapshot.clear();
System.out.println("Error reading snapshot:");
e.printStackTrace(System.out);
}
}
}
private static Path toCopyOperationTarget(Path path) {
Path rhs;
if(path.startsWith("projects/IDE-all-extras/extras")) {
Path subpath = path.subpath(3, path.getNameCount());
rhs = fromZipPath(subpath);
} else if(path.startsWith("libraries/orbit-sources/com.google.gwt.servlet")) {
Path subpath = path.subpath(3, path.getNameCount());
rhs = fromZipPath("plugins/com.google.gwt.servlet.jar").resolve(subpath);
} else {
Path subpath = path.subpath(4, path.getNameCount());
if(path.startsWith("libraries/eclipse.pde.ui/ui/org.eclipse.pde.ui.templates")) {
rhs = fromZipPath("plugins/org.eclipse.pde.ui.templates.jar").resolve(subpath);
} else if(path.startsWith("libraries/eclipse.jdt.core/org.eclipse.jdt.annotation_v1/src")) {
rhs = fromZipPath("plugins/org.eclipse.jdt.annotation_v1.jar/src").resolve(subpath);
} else if(path.startsWith("libraries/eclipse.jdt.core/org.eclipse.jdt.annotation/src")) {
rhs = fromZipPath("plugins/org.eclipse.jdt.annotation.jar/src").resolve(subpath);
} else {
rhs = fromZipPath(subpath);
}
}
return rhs;
}
static Path fromZipPath(String path) {
return fromZipPath(Paths.get(path));
}
static Path fromZipPath(Path path) {
return path;
}
private static Path toZipPath(Path path) {
return path;
}
private static ArrayList<Path> streamAllFiles(Path root) throws IOException {
ArrayList<Path> folders = new ArrayList<>();
for(Path project : Files.list(root.resolve("projects")).filter(Files::isDirectory).toList()) {
Path projectBin = project.resolve("bin");
if(Files.isDirectory(projectBin)) {
ArrayList<Path> projectBins = Files.list(projectBin).filter(Files::isDirectory).toList();
folders.addAll(projectBins);
}
}
for(String template : Arrays.asList("templates_3.0", "templates_3.1", "templates_3.3", "templates_3.5")) {
Path folder = root.resolve("libraries/eclipse.pde.ui/ui/org.eclipse.pde.ui.templates").resolve(template);
if(Files.isDirectory(folder)) {
folders.add(folder);
}
}
folders.add(root.resolve("libraries/eclipse.jdt.core/org.eclipse.jdt.annotation_v1/src"));
folders.add(root.resolve("libraries/eclipse.jdt.core/org.eclipse.jdt.annotation/src"));
folders.add(root.resolve("libraries/orbit-sources/com.google.gwt.servlet/com"));
folders.add(root.resolve("projects/IDE-all-extras/extras"));
ArrayList<Path> all = new ArrayList<>(folders.size() * 100);
for(Path folder : folders) {
all.addAll(Files.walk(folder).filter(Files::isRegularFile).toList());
}
return all;
}
private static boolean isWatchedFile(Path path) {
if(path.getNameCount() < 5) {
if(path.getNameCount() == 4 && path.startsWith("projects/IDE-all-extras/extras")) {
return path.getFileName().toString().equals(".instructions.txt") == false;
}
return false;
}
if(path.startsWith("libraries/eclipse.jdt.core/org.eclipse.jdt.annotation_v1/src")) {
return true;
}
if(path.startsWith("libraries/eclipse.jdt.core/org.eclipse.jdt.annotation/src")) {
return true;
}
if(path.startsWith("libraries/orbit-sources/com.google.gwt.servlet")) {
return true;
}
if(path.startsWith("libraries/eclipse.pde.ui/ui/org.eclipse.pde.ui.templates")) {
switch(path.getName(4).toString()) {
case "templates_3.0":
case "templates_3.1":
case "templates_3.3":
case "templates_3.5":
return true;
default:
return false;
}
}
if(path.startsWith("projects")) {
if(path.startsWith("projects/IDE-all-extras/extras")) {
return path.getName(3).toString().startsWith(".") == false;
}
return path.getName(2).toString().equals("bin");
}
return false;
}
private static void reportTime(String phase, int progress, int total) throws InterruptedException {
boolean done = progress == total && oldPhase == phase;
if(done || System.currentTimeMillis() > timeMillis) {
updateTime();
if(phase != oldPhase) {
phaseCounter = 0;
}
if(phaseCounter-- <= 0) {
phaseCounter = 9;
System.out.print("\n");
if(phase == oldPhase) {
System.out.print(oldPhase.replaceAll(".", " "));
}
}
if(phase != oldPhase) {
System.out.print(phase);
oldPhase = phase;
}
System.out.printf(" %.1f%%", 100.0 * progress / total);
if(Thread.interrupted()) {
Thread.currentThread().interrupt();
throw new InterruptedException();
}
}
}
private static void updateTime() {
timeMillis = System.currentTimeMillis() + 1000;
}
public static Path determineUnusedTarget() {
if(examineIdeTarget(TARGET_IDE1)) {
return TARGET_IDE1;
}
if(examineIdeTarget(TARGET_IDE2)) {
return TARGET_IDE2;
}
return TARGET_IDE3;
}
private static boolean examineIdeTarget(Path target) {
Path folder = target.resolve("configuration/org.eclipse.equinox.app/.manager");
if(Files.isDirectory(folder) == false) {
return true;
}
try {
if(isSameAsRunning(target)) {
return false;
}
return Files.list(folder).noneMatch(p -> p.getFileName().toString().matches("\\.tmp\\d+\\.instance"));
} catch(IOException e) {
System.out.print("Could not examine folder contents in " + folder);
e.printStackTrace(System.out);
return false;
}
}
private static boolean isRunningSelfHosted() {
return isSameAsRunning(TARGET_IDE1) || isSameAsRunning(TARGET_IDE2) || isSameAsRunning(TARGET_IDE3);
}
private static boolean isSameAsRunning(Path target) {
return Paths.get(RUNNING).toAbsolutePath().equals(target.toAbsolutePath());
}
private static Map<String, String> environment() {
Map<String, String> map = Map.from(System.getenv());
Path target = Paths.get("target/logs").toAbsolutePath();
String prefix = "build_";
if(map.containsKey("build")) {
ArrayList<String> lines = map.keySet().toArrayList().sort().replaceAll(s -> s + "=" + map.get(s)).add("");
try {
Files.createDirectories(target);
String timestamp2 = DateTimeFormatter.ofPattern("yyyy_MM_dd_HH_mm_ss", ENGLISH).format(NOW);
Files.write(target.resolve(prefix + timestamp2 + ".properties"), lines, UTF8);
} catch(IOException e) {
e.printStackTrace(System.out);
System.out.println("Could not create log of build environment, proceeding anyway...");
}
} else if(Files.isDirectory(target)) {
IOStream<Path> stream =
Files.list(target).filter(p -> p.getFileName().toString().startsWith(prefix)).filter(
Files::isRegularFile).sorted().limit(1);
ArrayList<ArrayList<String>> envs;
try {
envs = stream.toList().map(Files::readAllLines);
} catch(IOException e) {
throw new RuntimeException(e);
}
if(envs.notEmpty()) {
ArrayList<String> lines = envs.get(0).filter(s -> s.contains("="));
HashMap<String, ArrayList<String>> multiMap =
lines.stream().toMultiMap(s -> s.substring(0, s.indexOf('=')), s -> s.substring(s.indexOf('=') + 1));
HashMap<String, String> hashMap = multiMap.entrySet().stream().toMap(Pair::lhs, p -> p.rhs.get(0));
return hashMap.toMap();
}
}
return map;
}
}