mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
jps-bootstrap: download classes from compile inc configuration
GitOrigin-RevId: 56393e4b6dc38f085b49f0114ea7d445da100183
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4a46b62742
commit
e5a1154ba5
@@ -15,11 +15,31 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>31.0.1-jre</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.15</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.21</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jetbrains.intellij.platform</groupId>
|
||||
<artifactId>jps-model</artifactId>
|
||||
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.jpsBootstrap;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// This currently a copy of BuildDependenciesDownloader.groovy. In the feature both places will share one java implementation
|
||||
final class BuildDependenciesDownloader {
|
||||
// Add something to file name computation to make a different name than BuildDependenciesDownloader.groovy
|
||||
private static final String JPS_BOOTSTRAP_SALT = "jps-bootstrap";
|
||||
|
||||
private static final String HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
private static final HttpClient httpClient = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build();
|
||||
|
||||
static void debug(String message) {
|
||||
JpsBootstrapUtil.verbose(message);
|
||||
}
|
||||
|
||||
static void info(String message) {
|
||||
JpsBootstrapUtil.info(message);
|
||||
}
|
||||
|
||||
static void checkCommunityRoot(Path communityRoot) {
|
||||
if (communityRoot == null) {
|
||||
throw new IllegalStateException("passed community root is null");
|
||||
}
|
||||
|
||||
Path probeFile = communityRoot.resolve("intellij.idea.community.main.iml");
|
||||
if (!Files.exists(probeFile)) {
|
||||
throw new IllegalStateException("community root was not found at " + communityRoot);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getProjectLocalDownloadCache(Path communityRoot) {
|
||||
Path projectLocalDownloadCache = communityRoot.resolve("build").resolve("download");
|
||||
|
||||
try {
|
||||
Files.createDirectories(projectLocalDownloadCache);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return projectLocalDownloadCache;
|
||||
}
|
||||
|
||||
private static Path getDownloadCachePath(Path communityRoot) throws IOException {
|
||||
checkCommunityRoot(communityRoot);
|
||||
|
||||
Path path;
|
||||
if (JpsBootstrapUtil.underTeamCity) {
|
||||
String persistentCachePath = JpsBootstrapUtil.getTeamCitySystemProperties().getProperty("agent.persistent.cache");
|
||||
if (persistentCachePath == null || persistentCachePath.isBlank()) {
|
||||
throw new IllegalStateException("'agent.persistent.cache' system property is required under TeamCity");
|
||||
}
|
||||
path = Paths.get(persistentCachePath);
|
||||
}
|
||||
else {
|
||||
path = getProjectLocalDownloadCache(communityRoot);
|
||||
}
|
||||
|
||||
Files.createDirectories(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
static synchronized Path downloadFileToCacheLocation(Path communityRoot, URI uri) throws IOException, InterruptedException {
|
||||
String uriString = uri.toString();
|
||||
String lastNameFromUri = uriString.substring(uriString.lastIndexOf('/') + 1);
|
||||
String fileName = DigestUtils.sha256Hex(JPS_BOOTSTRAP_SALT + uriString).substring(0, 10) + "-" + lastNameFromUri;
|
||||
Path targetFile = getDownloadCachePath(communityRoot).resolve(fileName);
|
||||
|
||||
downloadFile(uri, targetFile);
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
static synchronized Path extractFileToCacheLocation(Path communityRoot, Path archiveFile) {
|
||||
try {
|
||||
String directoryName = DigestUtils.sha256Hex(JPS_BOOTSTRAP_SALT + archiveFile).substring(0, 6) + "-" + archiveFile.getFileName().toString();
|
||||
Path cacheDirectory = getDownloadCachePath(communityRoot).resolve(directoryName + ".d");
|
||||
|
||||
// Maintain one top-level directory (cacheDirectory) under persistent cache directory, since
|
||||
// TeamCity removes whole top-level directories upon cleanup, so both flag and extract directory
|
||||
// will be deleted at the same time
|
||||
Path flagFile = cacheDirectory.resolve(".flag.jps-bootstrap");
|
||||
Path extractDirectory = cacheDirectory.resolve(archiveFile.getFileName().toString() + ".d");
|
||||
extractFileWithFlagFileLocation(archiveFile, extractDirectory, flagFile);
|
||||
|
||||
// Update file modification time to maintain FIFO caches i.e.
|
||||
// in persistent cache folder on TeamCity agent
|
||||
Files.setLastModifiedTime(cacheDirectory, FileTime.from(Instant.now()));
|
||||
|
||||
return extractDirectory;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getExpectedFlagFileContent(Path archiveFile, Path targetDirectory) throws IOException {
|
||||
// Increment this number to force all clients to extract content again
|
||||
// e.g. when some issues in extraction code were fixed
|
||||
int codeVersion = 2;
|
||||
|
||||
long numberOfTopLevelEntries;
|
||||
try (Stream<Path> pathStream = Files.list(targetDirectory)) {
|
||||
numberOfTopLevelEntries = pathStream.count();
|
||||
}
|
||||
|
||||
return (codeVersion + "\n" + archiveFile.toString() + "\n" +
|
||||
"topLevelDirectoryEntries:" + numberOfTopLevelEntries).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static boolean checkFlagFile(Path archiveFile, Path flagFile, Path targetDirectory) throws IOException {
|
||||
if (!Files.isRegularFile(flagFile) || !Files.isDirectory(targetDirectory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] existingContent = Files.readAllBytes(flagFile);
|
||||
return Arrays.equals(existingContent, getExpectedFlagFileContent(archiveFile, targetDirectory));
|
||||
}
|
||||
|
||||
// assumes file at `archiveFile` is immutable
|
||||
private static void extractFileWithFlagFileLocation(Path archiveFile, Path targetDirectory, Path flagFile) throws IOException {
|
||||
if (checkFlagFile(archiveFile, flagFile, targetDirectory)) {
|
||||
debug("Skipping extract to " + targetDirectory + " since flag file " + flagFile + " is correct");
|
||||
|
||||
// Update file modification time to maintain FIFO caches i.e.
|
||||
// in persistent cache folder on TeamCity agent
|
||||
Files.setLastModifiedTime(targetDirectory, FileTime.from(Instant.now()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Files.exists(targetDirectory)) {
|
||||
if (!Files.isDirectory(targetDirectory)) {
|
||||
throw new IllegalStateException("Target '" + targetDirectory + "' exists, but it's not a directory. Please delete it manually");
|
||||
}
|
||||
|
||||
BuildDependenciesUtil.cleanDirectory(targetDirectory);
|
||||
}
|
||||
|
||||
info(" * Extracting " + archiveFile + " to " + targetDirectory);
|
||||
|
||||
Files.createDirectories(targetDirectory);
|
||||
BuildDependenciesUtil.extractZip(archiveFile, targetDirectory);
|
||||
|
||||
Files.write(flagFile, getExpectedFlagFileContent(archiveFile, targetDirectory));
|
||||
if (!checkFlagFile(archiveFile, flagFile, targetDirectory)) {
|
||||
throw new IllegalStateException("checkFlagFile must be true right after extracting the archive. " +
|
||||
"flagFile:" + flagFile +
|
||||
"archiveFile:" + archiveFile +
|
||||
"target:" + targetDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFile(URI uri, Path target) throws IOException, InterruptedException {
|
||||
Instant now = Instant.now();
|
||||
if (Files.exists(target)) {
|
||||
// Update file modification time to maintain FIFO caches i.e.
|
||||
// in persistent cache folder on TeamCity agent
|
||||
Files.setLastModifiedTime(target, FileTime.from(now));
|
||||
return;
|
||||
}
|
||||
|
||||
// save to the same disk to ensure that move will be atomic and not as a copy
|
||||
String tempFileName = target.getFileName() + "-"
|
||||
+ Long.toString(now.getEpochSecond() - 1634886185, 36) + "-"
|
||||
+ Integer.toString(now.getNano(), 36);
|
||||
|
||||
if (tempFileName.length() > 255) {
|
||||
tempFileName = tempFileName.substring(tempFileName.length() - 255);
|
||||
}
|
||||
Path tempFile = target.getParent().resolve(tempFileName);
|
||||
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(uri)
|
||||
.setHeader("User-Agent", "Build Script Downloader")
|
||||
.build();
|
||||
|
||||
HttpResponse<Path> response = httpClient.send(request, HttpResponse.BodyHandlers.ofFile(tempFile));
|
||||
if (response.statusCode() != 200) {
|
||||
throw new IllegalStateException("Error downloading " + uri + ": non-200 http status code " + response.statusCode());
|
||||
}
|
||||
|
||||
long contentLength = response.headers().firstValueAsLong(HTTP_HEADER_CONTENT_LENGTH).orElse(-1);
|
||||
if (contentLength <= 0) {
|
||||
throw new IllegalStateException("Header '" + HTTP_HEADER_CONTENT_LENGTH + "' is missing or zero for " + uri);
|
||||
}
|
||||
|
||||
long fileSize = Files.size(tempFile);
|
||||
if (fileSize != contentLength) {
|
||||
throw new IllegalStateException("Wrong file length after downloading uri '" + uri +
|
||||
"' to '" + tempFile +
|
||||
"': expected length " + contentLength +
|
||||
"from Content-Length header, but got " + fileSize + " on disk");
|
||||
}
|
||||
|
||||
Files.move(tempFile, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
finally {
|
||||
Files.deleteIfExists(tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.jpsBootstrap;
|
||||
|
||||
import com.google.common.io.MoreFiles;
|
||||
import com.google.common.io.RecursiveDeleteOption;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
final class BuildDependenciesUtil {
|
||||
static boolean isPosix = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
|
||||
|
||||
static void extractZip(Path archiveFile, Path target) throws IOException {
|
||||
try (ZipFile zipFile = new ZipFile(archiveFile.toFile(), "UTF-8")) {
|
||||
for (final Enumeration<ZipArchiveEntry> en = zipFile.getEntries(); en.hasMoreElements(); ) {
|
||||
ZipArchiveEntry entry = en.nextElement();
|
||||
|
||||
Path entryPath = entryFile(target, entry.getName());
|
||||
if (entry.isDirectory()) {
|
||||
Files.createDirectories(entryPath);
|
||||
}
|
||||
else {
|
||||
try (InputStream is = zipFile.getInputStream(entry)) {
|
||||
Files.copy(is, entryPath);
|
||||
}
|
||||
|
||||
//noinspection OctalInteger
|
||||
if (isPosix && (entry.getUnixMode() & 0111) != 0) {
|
||||
//noinspection SpellCheckingInspection
|
||||
Files.setPosixFilePermissions(entryPath, PosixFilePermissions.fromString("rwxr-xr-x"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Path entryFile(Path outputDir, String entryName) throws IOException {
|
||||
ensureValidPath(entryName);
|
||||
return outputDir.resolve(trimStart(entryName, '/'));
|
||||
}
|
||||
|
||||
private static void ensureValidPath(String entryName) throws IOException {
|
||||
if (entryName.contains("..") && Arrays.asList(entryName.split("[/\\\\]")).contains("..")) {
|
||||
throw new IOException("Invalid entry name: " + entryName);
|
||||
}
|
||||
}
|
||||
|
||||
private static String trimStart(String s, char charToTrim) {
|
||||
int index = 0;
|
||||
while (s.charAt(index) == charToTrim) index++;
|
||||
return s.substring(index);
|
||||
}
|
||||
|
||||
static void cleanDirectory(Path directory) throws IOException {
|
||||
Files.createDirectories(directory);
|
||||
try (Stream<Path> stream = Files.list(directory)) {
|
||||
stream.forEach(path -> {
|
||||
try {
|
||||
MoreFiles.deleteRecursively(path, RecursiveDeleteOption.ALLOW_INSECURE);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.jpsBootstrap;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import groovy.transform.CompileStatic;
|
||||
import org.jetbrains.jps.model.JpsProject;
|
||||
import org.jetbrains.jps.model.java.*;
|
||||
import org.jetbrains.jps.model.module.JpsModule;
|
||||
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
|
||||
import org.jetbrains.jps.util.JpsPathUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jetbrains.jpsBootstrap.JpsBootstrapUtil.verbose;
|
||||
|
||||
public class ClassesFromCompileInc {
|
||||
public final static String MANIFEST_JSON_URL_ENV_NAME = "JPS_BOOTSTRAP_MANIFEST_JSON_URL";
|
||||
|
||||
public static void downloadProjectClasses(JpsProject project, Path communityRoot) throws IOException, InterruptedException {
|
||||
String manifestUrl = System.getenv(MANIFEST_JSON_URL_ENV_NAME);
|
||||
if (manifestUrl == null || manifestUrl.isBlank()) {
|
||||
throw new IllegalStateException("Env variable '" + MANIFEST_JSON_URL_ENV_NAME + "' is missing or empty");
|
||||
}
|
||||
verbose("Got manifest json url '" + manifestUrl + "' from $" + MANIFEST_JSON_URL_ENV_NAME);
|
||||
|
||||
final Path manifest = BuildDependenciesDownloader.downloadFileToCacheLocation(communityRoot, URI.create(manifestUrl));
|
||||
Map<String, Path> parts = downloadPartsFromMetadataJson(manifest, communityRoot);
|
||||
assignModuleOutputs(project, parts);
|
||||
}
|
||||
|
||||
private static void assignModuleOutputs(JpsProject project, Map<String, Path> parts) {
|
||||
Map<String, Path> partsCopy = new HashMap<>(parts);
|
||||
|
||||
for (JpsModule module : project.getModules()) {
|
||||
boolean hasProduction = false;
|
||||
boolean hasTests = false;
|
||||
|
||||
for (JpsModuleSourceRoot sourceRoot : module.getSourceRoots()) {
|
||||
if (sourceRoot.getRootType() == JavaSourceRootType.SOURCE || sourceRoot.getRootType() == JavaResourceRootType.RESOURCE) {
|
||||
hasProduction = true;
|
||||
} else if (sourceRoot.getRootType() == JavaSourceRootType.TEST_SOURCE || sourceRoot.getRootType() == JavaResourceRootType.TEST_RESOURCE) {
|
||||
hasTests = true;
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported source root type " + sourceRoot + " for module " + module.getName());
|
||||
}
|
||||
}
|
||||
|
||||
final JpsJavaModuleExtension javaExtension = JpsJavaExtensionService.getInstance().getOrCreateModuleExtension(module);
|
||||
if (hasTests) {
|
||||
final String key = "test/" + module.getName();
|
||||
Path testOutputPath = partsCopy.remove(key);
|
||||
if (testOutputPath == null) {
|
||||
throw new IllegalStateException("Output for " + key + " was not found");
|
||||
}
|
||||
|
||||
javaExtension.setTestOutputUrl(JpsPathUtil.pathToUrl(testOutputPath.toString()));
|
||||
}
|
||||
|
||||
if (hasProduction) {
|
||||
final String key = "production/" + module.getName();
|
||||
Path productionOutputPath = partsCopy.remove(key);
|
||||
if (productionOutputPath == null) {
|
||||
throw new IllegalStateException("Output for " + key + " was not found");
|
||||
}
|
||||
|
||||
javaExtension.setOutputUrl(JpsPathUtil.pathToUrl(productionOutputPath.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!partsCopy.isEmpty()) {
|
||||
throw new IllegalStateException("After processing all project modules some entries left: " +
|
||||
String.join(" ", partsCopy.keySet()));
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Path> downloadPartsFromMetadataJson(Path metadataJson, Path communityRoot) throws InterruptedException, IOException {
|
||||
CompilationPartsMetadata partsMetadata;
|
||||
try (BufferedReader manifestReader = Files.newBufferedReader(metadataJson, StandardCharsets.UTF_8)) {
|
||||
partsMetadata = new Gson().fromJson(manifestReader, CompilationPartsMetadata.class);
|
||||
}
|
||||
|
||||
if (partsMetadata.files.isEmpty()) {
|
||||
throw new IllegalStateException("partsMetadata.files is empty, check " + metadataJson);
|
||||
}
|
||||
|
||||
List<Callable<Pair<String, Path>>> tasks = new ArrayList<>();
|
||||
for (final Map.Entry<String, String> entry : partsMetadata.files.entrySet()) {
|
||||
Callable<Pair<String, Path>> c = () -> {
|
||||
String modulePrefix = entry.getKey();
|
||||
String hash = entry.getValue();
|
||||
|
||||
URI outputPartUri = URI.create(partsMetadata.serverUrl + "/" + partsMetadata.prefix + "/" + modulePrefix + "/" + hash + ".jar");
|
||||
final Path outputPart = BuildDependenciesDownloader.downloadFileToCacheLocation(communityRoot, outputPartUri);
|
||||
final Path outputPartExtracted = BuildDependenciesDownloader.extractFileToCacheLocation(communityRoot, outputPart);
|
||||
|
||||
System.out.println(modulePrefix + " = " + outputPartExtracted);
|
||||
|
||||
return Pair.pair(modulePrefix, outputPartExtracted);
|
||||
};
|
||||
tasks.add(c);
|
||||
}
|
||||
|
||||
return JpsBootstrapUtil.executeTasksInParallel(tasks)
|
||||
.stream().collect(Collectors.toUnmodifiableMap(pair -> pair.getFirst(), pair -> pair.getSecond()));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
downloadPartsFromMetadataJson(Paths.get("D:\\temp\\metadata.json"), Paths.get("D:\\Work\\intellij\\community"));
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
private static final class CompilationPartsMetadata {
|
||||
@SerializedName("server-url")
|
||||
public String serverUrl;
|
||||
public String prefix;
|
||||
|
||||
/**
|
||||
* Map compilation part path to a hash, for now SHA-256 is used.
|
||||
* sha256(file) == hash, though that may be changed in the future.
|
||||
*/
|
||||
public Map<String, String> files;
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.jpsBootstrap;
|
||||
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.groovy.compiler.rt.GroovyRtConstants;
|
||||
import org.jetbrains.jps.api.GlobalOptions;
|
||||
import org.jetbrains.jps.build.Standalone;
|
||||
import org.jetbrains.jps.incremental.groovy.JpsGroovycRunner;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.model.JpsModel;
|
||||
import org.jetbrains.jps.model.JpsNamedElement;
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
|
||||
import org.jetbrains.jps.model.module.JpsModule;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jetbrains.jpsBootstrap.JpsBootstrapUtil.*;
|
||||
import static org.jetbrains.jpsBootstrap.JpsBootstrapUtil.fatal;
|
||||
|
||||
public class ClassesFromJpsBuild {
|
||||
public static final String CLASSES_FROM_JPS_BUILD_ENV_NAME = "JPS_BOOTSTRAP_CLASSES_FROM_JPS_BUILD";
|
||||
|
||||
public static void buildModule(JpsModule module, Path projectHome, JpsModel model, Path jpsBootstrapWorkDir) throws Exception {
|
||||
// Workaround for KTIJ-19065
|
||||
System.setProperty(PathManager.PROPERTY_HOME_PATH, projectHome.toString());
|
||||
|
||||
System.setProperty("kotlin.incremental.compilation", "true");
|
||||
System.setProperty("kotlin.daemon.enabled", "false");
|
||||
System.setProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "true");
|
||||
|
||||
System.setProperty(JpsGroovycRunner.GROOVYC_IN_PROCESS, "true");
|
||||
System.setProperty(GroovyRtConstants.GROOVYC_ASM_RESOLVING_ONLY, "false");
|
||||
System.setProperty(GlobalOptions.USE_DEFAULT_FILE_LOGGING_OPTION, "true");
|
||||
System.setProperty(GlobalOptions.LOG_DIR_OPTION, jpsBootstrapWorkDir.resolve("log").toString());
|
||||
|
||||
String url = "file://" + FileUtilRt.toSystemIndependentName(jpsBootstrapWorkDir.resolve("out").toString());
|
||||
JpsJavaExtensionService.getInstance().getOrCreateProjectExtension(model.getProject()).setOutputUrl(url);
|
||||
|
||||
info("Compilation log directory: " + System.getProperty(GlobalOptions.LOG_DIR_OPTION));
|
||||
|
||||
// kotlin.util.compiler-dependencies downloads all dependencies required for running Kotlin JPS compiler
|
||||
// see org.jetbrains.kotlin.idea.artifacts.KotlinArtifactsFromSources
|
||||
runBuild(model, jpsBootstrapWorkDir, "kotlin.util.compiler-dependencies");
|
||||
|
||||
runBuild(model, jpsBootstrapWorkDir, module.getName());
|
||||
}
|
||||
|
||||
private static void runBuild(JpsModel model, Path workDir, String moduleName) throws Exception {
|
||||
final long buildStart = System.currentTimeMillis();
|
||||
final AtomicReference<String> firstError = new AtomicReference<>();
|
||||
|
||||
Path dataStorageRoot = workDir.resolve("jps-build-data");
|
||||
final Set<String> moduleNames = model.getProject().getModules().stream().map(JpsNamedElement::getName).collect(Collectors.toUnmodifiableSet());
|
||||
Standalone.runBuild(
|
||||
() -> model,
|
||||
dataStorageRoot.toFile(),
|
||||
false,
|
||||
ContainerUtil.set(moduleName),
|
||||
false,
|
||||
Collections.emptyList(),
|
||||
false,
|
||||
msg -> {
|
||||
BuildMessage.Kind kind = msg.getKind();
|
||||
String text = msg.toString();
|
||||
|
||||
switch (kind) {
|
||||
case PROGRESS:
|
||||
verbose(text);
|
||||
break;
|
||||
case WARNING:
|
||||
warn(text);
|
||||
case ERROR:
|
||||
case INTERNAL_BUILDER_ERROR:
|
||||
error(text);
|
||||
break;
|
||||
default:
|
||||
if (!msg.getMessageText().isBlank()) {
|
||||
if (moduleNames.contains(msg.getMessageText())) {
|
||||
verbose(text);
|
||||
}
|
||||
else {
|
||||
info(text);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ((kind == BuildMessage.Kind.ERROR || kind == BuildMessage.Kind.INTERNAL_BUILDER_ERROR)) {
|
||||
firstError.compareAndSet(null, text);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
System.out.println("Finished building '" + moduleName + "' in " + (System.currentTimeMillis() - buildStart) + " ms");
|
||||
|
||||
String firstErrorText = firstError.get();
|
||||
if (firstErrorText != null) {
|
||||
fatal("Build finished with errors. First error:\n" + firstErrorText);
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-190
@@ -2,42 +2,21 @@
|
||||
|
||||
package org.jetbrains.jpsBootstrap;
|
||||
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.URLUtil;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.groovy.compiler.rt.GroovyRtConstants;
|
||||
import org.jetbrains.jps.api.GlobalOptions;
|
||||
import org.jetbrains.jps.build.Standalone;
|
||||
import org.jetbrains.jps.incremental.groovy.JpsGroovycRunner;
|
||||
import org.jetbrains.jps.incremental.messages.BuildMessage;
|
||||
import org.jetbrains.jps.model.JpsElementFactory;
|
||||
import org.jetbrains.jps.model.JpsModel;
|
||||
import org.jetbrains.jps.model.JpsNamedElement;
|
||||
import org.jetbrains.jps.model.java.JpsJavaDependenciesEnumerator;
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
|
||||
import org.jetbrains.jps.model.library.JpsLibrary;
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType;
|
||||
import org.jetbrains.jps.model.module.JpsModule;
|
||||
import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService;
|
||||
import org.jetbrains.jps.model.serialization.JpsPathVariablesConfiguration;
|
||||
import org.jetbrains.jps.model.serialization.JpsProjectLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jetbrains.jpsBootstrap.JpsBootstrapUtil.*;
|
||||
@@ -62,7 +41,7 @@ public class JpsBootstrapMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
mainImpl(args);
|
||||
new JpsBootstrapMain(args).main();
|
||||
System.exit(0);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
@@ -72,8 +51,14 @@ public class JpsBootstrapMain {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
||||
private static void mainImpl(String[] args) throws Throwable {
|
||||
private final Path projectHome;
|
||||
private final Path communityHome;
|
||||
private final String moduleNameToRun;
|
||||
private final String classNameToRun;
|
||||
private final Path jpsBootstrapWorkDir;
|
||||
private final String[] mainArgsToRun;
|
||||
|
||||
public JpsBootstrapMain(String[] args) throws IOException {
|
||||
CommandLine cmdline;
|
||||
try {
|
||||
cmdline = (new DefaultParser()).parse(createCliOptions(), args, true);
|
||||
@@ -84,28 +69,24 @@ public class JpsBootstrapMain {
|
||||
throw new IllegalStateException("NOT_REACHED");
|
||||
}
|
||||
|
||||
final String[] freeArgs = cmdline.getArgs();
|
||||
if (cmdline.hasOption(ARG_HELP) || freeArgs.length < 2) {
|
||||
final List<String> freeArgs = Arrays.asList(cmdline.getArgs());
|
||||
if (cmdline.hasOption(ARG_HELP) || freeArgs.size() < 2) {
|
||||
showUsagesAndExit();
|
||||
}
|
||||
|
||||
moduleNameToRun = freeArgs.get(0);
|
||||
classNameToRun = freeArgs.get(1);
|
||||
|
||||
JpsBootstrapUtil.setVerboseEnabled(cmdline.hasOption(ARG_VERBOSE));
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
String moduleName = freeArgs[0];
|
||||
String className = freeArgs[1];
|
||||
|
||||
String communityHomeString = System.getenv(COMMUNITY_HOME_ENV);
|
||||
if (communityHomeString == null) fatal("Please set " + COMMUNITY_HOME_ENV + " environment variable");
|
||||
|
||||
Path communityHome = Path.of(communityHomeString);
|
||||
communityHome = Path.of(communityHomeString);
|
||||
|
||||
Path communityCheckFile = communityHome.resolve("intellij.idea.community.main.iml");
|
||||
if (!Files.exists(communityCheckFile)) fatal(COMMUNITY_HOME_ENV + " is incorrect: " + communityCheckFile + " is missing");
|
||||
|
||||
Path projectHome;
|
||||
|
||||
Path ultimateCheckFile = communityHome.getParent().resolve("intellij.idea.ultimate.main.iml");
|
||||
if (Files.exists(ultimateCheckFile)) {
|
||||
projectHome = communityHome.getParent();
|
||||
@@ -115,100 +96,77 @@ public class JpsBootstrapMain {
|
||||
projectHome = communityHome;
|
||||
}
|
||||
|
||||
// Workaround for KTIJ-19065
|
||||
System.setProperty(PathManager.PROPERTY_HOME_PATH, projectHome.toString());
|
||||
|
||||
Path workDir;
|
||||
|
||||
if (System.getenv(JPS_BOOTSTRAP_WORK_DIR_ENV) != null) {
|
||||
workDir = Path.of(System.getenv(JPS_BOOTSTRAP_WORK_DIR_ENV));
|
||||
jpsBootstrapWorkDir = Path.of(System.getenv(JPS_BOOTSTRAP_WORK_DIR_ENV));
|
||||
}
|
||||
else {
|
||||
workDir = communityHome.resolve("out").resolve("jps-bootstrap");
|
||||
jpsBootstrapWorkDir = communityHome.resolve("out").resolve("jps-bootstrap");
|
||||
}
|
||||
|
||||
info("Working directory: " + workDir);
|
||||
info("Working directory: " + jpsBootstrapWorkDir);
|
||||
Files.createDirectories(jpsBootstrapWorkDir);
|
||||
|
||||
Files.createDirectories(workDir);
|
||||
mainArgsToRun = freeArgs.subList(2, freeArgs.size()).toArray(new String[0]);
|
||||
}
|
||||
|
||||
Path m2LocalRepository = Path.of(System.getProperty("user.home"), ".m2", "repository");
|
||||
JpsModel model = JpsElementFactory.getInstance().createModel();
|
||||
JpsPathVariablesConfiguration pathVariablesConfiguration =
|
||||
JpsModelSerializationDataService.getOrCreatePathVariablesConfiguration(model.getGlobal());
|
||||
pathVariablesConfiguration.addPathVariable(
|
||||
"MAVEN_REPOSITORY", FileUtilRt.toSystemIndependentName(m2LocalRepository.toAbsolutePath().toString()));
|
||||
|
||||
System.setProperty("kotlin.incremental.compilation", "true");
|
||||
System.setProperty("kotlin.daemon.enabled", "false");
|
||||
System.setProperty(GlobalOptions.COMPILE_PARALLEL_OPTION, "true");
|
||||
|
||||
Map<String, String> pathVariables = JpsModelSerializationDataService.computeAllPathVariables(model.getGlobal());
|
||||
JpsProjectLoader.loadProject(model.getProject(), pathVariables, projectHome.toString());
|
||||
System.out.println(
|
||||
"Loaded project " + projectHome + ": " +
|
||||
model.getProject().getModules().size() + " modules, " +
|
||||
model.getProject().getLibraryCollection().getLibraries().size() + " libraries in " +
|
||||
(System.currentTimeMillis() - startTime) + " ms");
|
||||
|
||||
addSdk(model, "corretto-11", System.getProperty("java.home"));
|
||||
|
||||
String url = "file://" + FileUtilRt.toSystemIndependentName(workDir.resolve("out").toString());
|
||||
JpsJavaExtensionService.getInstance().getOrCreateProjectExtension(model.getProject()).setOutputUrl(url);
|
||||
|
||||
System.setProperty(JpsGroovycRunner.GROOVYC_IN_PROCESS, "true");
|
||||
System.setProperty(GroovyRtConstants.GROOVYC_ASM_RESOLVING_ONLY, "false");
|
||||
System.setProperty(GlobalOptions.USE_DEFAULT_FILE_LOGGING_OPTION, "true");
|
||||
System.setProperty(GlobalOptions.LOG_DIR_OPTION, workDir.resolve("log").toString());
|
||||
System.out.println("Log: " + System.getProperty(GlobalOptions.LOG_DIR_OPTION));
|
||||
|
||||
// kotlin.util.compiler-dependencies downloads all dependencies required for running Kotlin JPS compiler
|
||||
// see org.jetbrains.kotlin.idea.artifacts.KotlinArtifactsFromSources
|
||||
runBuild(model, workDir, "kotlin.util.compiler-dependencies");
|
||||
|
||||
runBuild(model, workDir, moduleName);
|
||||
|
||||
JpsModule module = model.getProject().getModules()
|
||||
.stream()
|
||||
.filter(m -> moduleName.equals(m.getName()))
|
||||
.findFirst().orElseThrow();
|
||||
JpsJavaDependenciesEnumerator enumerator = JpsJavaExtensionService
|
||||
.dependencies(module)
|
||||
.runtimeOnly()
|
||||
.productionOnly()
|
||||
.recursively()
|
||||
.withoutSdk();
|
||||
|
||||
List<URL> roots = new ArrayList<>();
|
||||
for (File file : enumerator.classes().getRoots()) {
|
||||
URL toURL = file.toURI().toURL();
|
||||
roots.add(toURL);
|
||||
}
|
||||
roots.sort(Comparator.comparing(URL::toString));
|
||||
|
||||
for (URL rootUrl : roots) {
|
||||
verbose(" CLASSPATH " + rootUrl);
|
||||
}
|
||||
private void main() throws Throwable {
|
||||
JpsModel model = JpsProjectUtils.loadJpsProject(projectHome);
|
||||
JpsModule module = JpsProjectUtils.getModuleByName(model, moduleNameToRun);
|
||||
|
||||
loadClasses(module, model);
|
||||
setSystemPropertiesFromTeamCityBuild();
|
||||
runMainFromModuleRuntimeClasspath(classNameToRun, mainArgsToRun, module);
|
||||
}
|
||||
|
||||
info("Running class " + className + " from module " + moduleName);
|
||||
private void loadClasses(JpsModule module, JpsModel model) throws Throwable {
|
||||
String fromJpsBuildEnvValue = System.getenv(ClassesFromJpsBuild.CLASSES_FROM_JPS_BUILD_ENV_NAME);
|
||||
boolean jpsBuild = fromJpsBuildEnvValue != null && JpsBootstrapUtil.toBooleanChecked(fromJpsBuildEnvValue);
|
||||
|
||||
try (URLClassLoader classloader = new URLClassLoader(roots.toArray(new URL[0]), ClassLoader.getPlatformClassLoader())) {
|
||||
String manifestJsonUrl = System.getenv(ClassesFromCompileInc.MANIFEST_JSON_URL_ENV_NAME);
|
||||
|
||||
if (jpsBuild && manifestJsonUrl != null) {
|
||||
throw new IllegalStateException("Both env. variables are set, choose only one: " +
|
||||
ClassesFromJpsBuild.CLASSES_FROM_JPS_BUILD_ENV_NAME + " " +
|
||||
ClassesFromCompileInc.MANIFEST_JSON_URL_ENV_NAME);
|
||||
}
|
||||
|
||||
if (!jpsBuild && manifestJsonUrl == null) {
|
||||
// Nothing specified. It's ok locally, but on buildserver we must be sure
|
||||
if (underTeamCity) {
|
||||
throw new IllegalStateException("On buildserver one of the following env. variables must be set: " +
|
||||
ClassesFromJpsBuild.CLASSES_FROM_JPS_BUILD_ENV_NAME + " " +
|
||||
ClassesFromCompileInc.MANIFEST_JSON_URL_ENV_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (manifestJsonUrl != null) {
|
||||
info("Downloading project classes from " + manifestJsonUrl);
|
||||
ClassesFromCompileInc.downloadProjectClasses(model.getProject(), communityHome);
|
||||
} else {
|
||||
ClassesFromJpsBuild.buildModule(module, projectHome, model, jpsBootstrapWorkDir);
|
||||
}
|
||||
}
|
||||
|
||||
private static void runMainFromModuleRuntimeClasspath(String className, String[] args, JpsModule module) throws Throwable {
|
||||
List<URL> moduleRuntimeClasspath = JpsProjectUtils.getModuleRuntimeClasspath(module);
|
||||
verbose("Module " + module.getName() + " classpath:\n " + moduleRuntimeClasspath.stream().map(URL::toString).collect(Collectors.joining("\n ")));
|
||||
|
||||
info("Running class " + className + " from module " + module.getName());
|
||||
try (URLClassLoader classloader = new URLClassLoader(moduleRuntimeClasspath.toArray(new URL[0]), ClassLoader.getPlatformClassLoader())) {
|
||||
Class<?> mainClass;
|
||||
try {
|
||||
mainClass = classloader.loadClass(className);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
for (URL rootUrl : roots) {
|
||||
info(" CLASSPATH " + rootUrl);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Class '" + className + "' was not found. See the class path above");
|
||||
final String message = "Class '" + className + "' was not found in runtime classpath of module " + module.getName();
|
||||
info(message + ":\n " + moduleRuntimeClasspath.stream().map(URL::toString).collect(Collectors.joining("\n ")));
|
||||
throw new IllegalStateException(message + ". See the class path above");
|
||||
}
|
||||
|
||||
//noinspection ConfusingArgumentToVarargsMethod
|
||||
MethodHandles.lookup()
|
||||
.findStatic(mainClass, "main", MethodType.methodType(Void.TYPE, String[].class))
|
||||
.invokeExact(Arrays.copyOfRange(freeArgs, 2, freeArgs.length));
|
||||
.invokeExact(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,85 +182,6 @@ public class JpsBootstrapMain {
|
||||
}
|
||||
}
|
||||
|
||||
private static void runBuild(JpsModel model, Path workDir, String moduleName) throws Exception {
|
||||
final long buildStart = System.currentTimeMillis();
|
||||
final AtomicReference<String> firstError = new AtomicReference<>();
|
||||
|
||||
Path dataStorageRoot = workDir.resolve("jps-build-data");
|
||||
final Set<String> moduleNames = model.getProject().getModules().stream().map(JpsNamedElement::getName).collect(Collectors.toUnmodifiableSet());
|
||||
Standalone.runBuild(
|
||||
() -> model,
|
||||
dataStorageRoot.toFile(),
|
||||
false,
|
||||
ContainerUtil.set(moduleName),
|
||||
false,
|
||||
Collections.emptyList(),
|
||||
false,
|
||||
msg -> {
|
||||
BuildMessage.Kind kind = msg.getKind();
|
||||
String text = msg.toString();
|
||||
|
||||
switch (kind) {
|
||||
case PROGRESS:
|
||||
verbose(text);
|
||||
break;
|
||||
case WARNING:
|
||||
warn(text);
|
||||
case ERROR:
|
||||
case INTERNAL_BUILDER_ERROR:
|
||||
error(text);
|
||||
break;
|
||||
default:
|
||||
if (!msg.getMessageText().isBlank()) {
|
||||
if (moduleNames.contains(msg.getMessageText())) {
|
||||
verbose(text);
|
||||
}
|
||||
else {
|
||||
info(text);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ((kind == BuildMessage.Kind.ERROR || kind == BuildMessage.Kind.INTERNAL_BUILDER_ERROR)) {
|
||||
firstError.compareAndSet(null, text);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
System.out.println("Finished building '" + moduleName + "' in " + (System.currentTimeMillis() - buildStart) + " ms");
|
||||
|
||||
String firstErrorText = firstError.get();
|
||||
if (firstErrorText != null) {
|
||||
fatal("Build finished with errors. First error:\n" + firstErrorText);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> readModulesFromReleaseFile(Path jdkDir) throws IOException {
|
||||
Path releaseFile = jdkDir.resolve("release");
|
||||
Properties p = new Properties();
|
||||
try (InputStream is = Files.newInputStream(releaseFile)) {
|
||||
p.load(is);
|
||||
}
|
||||
String jbrBaseUrl = URLUtil.JRT_PROTOCOL + URLUtil.SCHEME_SEPARATOR +
|
||||
FileUtil.toSystemIndependentName(jdkDir.toFile().getAbsolutePath()) +
|
||||
URLUtil.JAR_SEPARATOR;
|
||||
String modules = p.getProperty("MODULES");
|
||||
return ContainerUtil.map(StringUtil.split(StringUtil.unquoteString(modules), " "), s -> jbrBaseUrl + s);
|
||||
}
|
||||
|
||||
private static void addSdk(JpsModel model, String sdkName, String sdkHome) throws IOException {
|
||||
JpsJavaExtensionService.getInstance().addJavaSdk(model.getGlobal(), sdkName, sdkHome);
|
||||
JpsLibrary additionalSdk = model.getGlobal().getLibraryCollection().findLibrary(sdkName);
|
||||
if (additionalSdk == null) {
|
||||
throw new IllegalStateException("SDK " + sdkHome + " was not found");
|
||||
}
|
||||
|
||||
for (String moduleUrl : readModulesFromReleaseFile(Path.of(sdkHome))) {
|
||||
additionalSdk.addRoot(moduleUrl, JpsOrderRootType.COMPILED);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("->fail")
|
||||
private static void showUsagesAndExit() {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
|
||||
+87
-3
@@ -10,12 +10,12 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class JpsBootstrapUtil {
|
||||
public static final String TEAMCITY_BUILD_PROPERTIES_FILE_ENV = "TEAMCITY_BUILD_PROPERTIES_FILE";
|
||||
public static final String TEAMCITY_CONFIGURATION_PROPERTIES_SYSTEM_PROPERTY = "teamcity.configuration.properties.file";
|
||||
|
||||
public static final boolean underTeamCity = System.getenv("TEAMCITY_VERSION") != null;
|
||||
|
||||
@@ -74,6 +74,15 @@ public class JpsBootstrapUtil {
|
||||
JpsBootstrapUtil.verboseEnabled = verboseEnabled;
|
||||
}
|
||||
|
||||
public static boolean toBooleanChecked(String s) {
|
||||
switch (s) {
|
||||
case "true": return true;
|
||||
case "false": return false;
|
||||
default:
|
||||
throw new IllegalArgumentException("Could not convert '" + s + "' to boolean. Only 'true' or 'false' values are accepted");
|
||||
}
|
||||
}
|
||||
|
||||
public static Properties getTeamCitySystemProperties() throws IOException {
|
||||
if (!underTeamCity) {
|
||||
throw new IllegalStateException("Not under TeamCity");
|
||||
@@ -91,4 +100,79 @@ public class JpsBootstrapUtil {
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static Properties getTeamCityConfigProperties() throws IOException {
|
||||
Properties systemProperties = getTeamCitySystemProperties();
|
||||
|
||||
final String configPropertiesFile = systemProperties.getProperty(TEAMCITY_CONFIGURATION_PROPERTIES_SYSTEM_PROPERTY);
|
||||
if (configPropertiesFile == null || configPropertiesFile.length() == 0) {
|
||||
throw new IllegalStateException("TeamCity system property '" + TEAMCITY_CONFIGURATION_PROPERTIES_SYSTEM_PROPERTY + "' is missing under TeamCity build");
|
||||
}
|
||||
|
||||
Properties properties = new Properties();
|
||||
try (BufferedReader reader = Files.newBufferedReader(Path.of(configPropertiesFile))) {
|
||||
properties.load(reader);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static String getTeamCityConfigPropertyOrThrow(String configProperty) throws IOException {
|
||||
final Properties properties = getTeamCityConfigProperties();
|
||||
final String value = properties.getProperty(configProperty);
|
||||
if (value == null) {
|
||||
throw new IllegalStateException("TeamCity config property " + configProperty + " was not found");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static <T> List<T> executeTasksInParallel(List<Callable<T>> tasks) throws InterruptedException {
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(5);
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
info("Executing " + tasks.size() + " in parallel");
|
||||
|
||||
List<Future<T>> futures = executorService.invokeAll(tasks);
|
||||
|
||||
List<Throwable> errors = new ArrayList<>();
|
||||
List<T> results = new ArrayList<>();
|
||||
for (Future<T> future : futures) {
|
||||
try {
|
||||
T r = future.get(10, TimeUnit.MINUTES);
|
||||
results.add(r);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
errors.add(e.getCause());
|
||||
if (errors.size() > 4) {
|
||||
executorService.shutdownNow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
throw new IllegalStateException("Timeout waiting for results, exiting");
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.size() > 0) {
|
||||
RuntimeException t = new RuntimeException("Unable to execute all targets, " + errors.size() + " error(s)");
|
||||
for (Throwable err : errors) {
|
||||
t.addSuppressed(err);
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
|
||||
if (results.size() != tasks.size()) {
|
||||
throw new IllegalStateException("received results size != tasks size (" + results.size() + " != " + tasks.size() + ")");
|
||||
}
|
||||
|
||||
return results;
|
||||
} finally {
|
||||
JpsBootstrapUtil.info("Finished all tasks in " + (System.currentTimeMillis() - start) + " ms");
|
||||
|
||||
if (!executorService.isShutdown()) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.jpsBootstrap;
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.URLUtil;
|
||||
import org.jetbrains.jps.model.JpsElementFactory;
|
||||
import org.jetbrains.jps.model.JpsModel;
|
||||
import org.jetbrains.jps.model.java.JpsJavaDependenciesEnumerator;
|
||||
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
|
||||
import org.jetbrains.jps.model.library.JpsLibrary;
|
||||
import org.jetbrains.jps.model.library.JpsOrderRootType;
|
||||
import org.jetbrains.jps.model.module.JpsModule;
|
||||
import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService;
|
||||
import org.jetbrains.jps.model.serialization.JpsPathVariablesConfiguration;
|
||||
import org.jetbrains.jps.model.serialization.JpsProjectLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public class JpsProjectUtils {
|
||||
public static JpsModel loadJpsProject(Path projectHome) throws Exception {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
Path m2LocalRepository = Path.of(System.getProperty("user.home"), ".m2", "repository");
|
||||
JpsModel model = JpsElementFactory.getInstance().createModel();
|
||||
JpsPathVariablesConfiguration pathVariablesConfiguration =
|
||||
JpsModelSerializationDataService.getOrCreatePathVariablesConfiguration(model.getGlobal());
|
||||
pathVariablesConfiguration.addPathVariable(
|
||||
"MAVEN_REPOSITORY", FileUtilRt.toSystemIndependentName(m2LocalRepository.toAbsolutePath().toString()));
|
||||
|
||||
Map<String, String> pathVariables = JpsModelSerializationDataService.computeAllPathVariables(model.getGlobal());
|
||||
JpsProjectLoader.loadProject(model.getProject(), pathVariables, projectHome.toString());
|
||||
System.out.println(
|
||||
"Loaded project " + projectHome + ": " +
|
||||
model.getProject().getModules().size() + " modules, " +
|
||||
model.getProject().getLibraryCollection().getLibraries().size() + " libraries in " +
|
||||
(System.currentTimeMillis() - startTime) + " ms");
|
||||
|
||||
addSdk(model, "corretto-11", System.getProperty("java.home"));
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public static JpsModule getModuleByName(JpsModel model, String moduleName) {
|
||||
return model.getProject().getModules()
|
||||
.stream()
|
||||
.filter(m -> moduleName.equals(m.getName()))
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException("Module " + moduleName + " is not found"));
|
||||
}
|
||||
|
||||
public static List<URL> getModuleRuntimeClasspath(JpsModule module) throws MalformedURLException {
|
||||
JpsJavaDependenciesEnumerator enumerator = JpsJavaExtensionService
|
||||
.dependencies(module)
|
||||
.runtimeOnly()
|
||||
.productionOnly()
|
||||
.recursively()
|
||||
.withoutSdk();
|
||||
|
||||
List<URL> roots = new ArrayList<>();
|
||||
for (File file : enumerator.classes().getRoots()) {
|
||||
URL toURL = file.toURI().toURL();
|
||||
roots.add(toURL);
|
||||
}
|
||||
roots.sort(Comparator.comparing(URL::toString));
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
private static void addSdk(JpsModel model, String sdkName, String sdkHome) throws IOException {
|
||||
JpsJavaExtensionService.getInstance().addJavaSdk(model.getGlobal(), sdkName, sdkHome);
|
||||
JpsLibrary additionalSdk = model.getGlobal().getLibraryCollection().findLibrary(sdkName);
|
||||
if (additionalSdk == null) {
|
||||
throw new IllegalStateException("SDK " + sdkHome + " was not found");
|
||||
}
|
||||
|
||||
for (String moduleUrl : readModulesFromReleaseFile(Path.of(sdkHome))) {
|
||||
additionalSdk.addRoot(moduleUrl, JpsOrderRootType.COMPILED);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> readModulesFromReleaseFile(Path jdkDir) throws IOException {
|
||||
Path releaseFile = jdkDir.resolve("release");
|
||||
Properties p = new Properties();
|
||||
try (InputStream is = Files.newInputStream(releaseFile)) {
|
||||
p.load(is);
|
||||
}
|
||||
String jbrBaseUrl = URLUtil.JRT_PROTOCOL + URLUtil.SCHEME_SEPARATOR +
|
||||
FileUtil.toSystemIndependentName(jdkDir.toFile().getAbsolutePath()) +
|
||||
URLUtil.JAR_SEPARATOR;
|
||||
String modules = p.getProperty("MODULES");
|
||||
return ContainerUtil.map(StringUtil.split(StringUtil.unquoteString(modules), " "), s -> jbrBaseUrl + s);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user