introduce BuildTargetHashSupplier (simplify build target fingerprint calculation)

GitOrigin-RevId: f810aed3a678cb36212d68c7539f6893bbe68cf2
This commit is contained in:
Vladimir Krivosheev
2024-08-22 16:36:31 +02:00
committed by intellij-monorepo-bot
parent 81e490cd6f
commit e3af69868a
27 changed files with 355 additions and 357 deletions

View File

@@ -56,11 +56,11 @@ public abstract class BuildTarget<R extends BuildRootDescriptor> {
* Allows the build target to tag the current project settings relevant to the build of this target
* (e.g., the language level of a Java module) so that the target is fully recompiled when those settings change.
*
* @param pd the complete state of a compilation invocation
* @param projectDescriptor the complete state of a compilation invocation
* @param out the print writer to which the project settings can be written (the settings are compared with the ones
* written during the invocation of the same method in a previous compilation).
*/
public void writeConfiguration(@NotNull ProjectDescriptor pd, @NotNull PrintWriter out) {
public void writeConfiguration(@NotNull ProjectDescriptor projectDescriptor, @NotNull PrintWriter out) {
}
/**

View File

@@ -0,0 +1,12 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.builders
import com.dynatrace.hash4j.hashing.HashSink
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.jps.cmdline.ProjectDescriptor
@ApiStatus.Experimental
@ApiStatus.Internal
interface BuildTargetHashSupplier {
fun computeConfigurationDigest(projectDescriptor: ProjectDescriptor, hash: HashSink)
}

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental
import com.dynatrace.hash4j.hashing.HashStream64
import com.dynatrace.hash4j.hashing.HashSink
import com.dynatrace.hash4j.hashing.Hashing
import com.intellij.openapi.util.SystemInfoRt
import com.intellij.openapi.util.text.StringUtilRt
@@ -15,11 +15,15 @@ import java.nio.file.StandardOpenOption
@Internal
@Throws(IOException::class)
fun getFileHash(file: Path): Long = getFileHash(file, Hashing.komihash5_0().hashStream())
fun getFileHash(file: Path): Long {
val hash = Hashing.komihash5_0().hashStream()
getFileHash(file, hash)
return hash.asLong
}
@Internal
@Throws(IOException::class)
fun getFileHash(file: Path, hash: HashStream64): Long {
fun getFileHash(file: Path, hash: HashSink) {
FileChannel.open(file, StandardOpenOption.READ).use { channel ->
val fileSize = channel.size()
val buffer = ByteBuffer.allocate(256 * 1024)
@@ -36,13 +40,12 @@ fun getFileHash(file: Path, hash: HashStream64): Long {
offset += readBytes
}
hash.putLong(fileSize)
return hash.asLong
}
}
/** path must be absolute ([Path.toAbsolutePath]), normalized ([Path.normalize]) and system-independent */
@Internal
fun normalizedPathHashCode(path: String, hash: HashStream64) {
fun normalizedPathHashCode(path: String, hash: HashSink) {
if (path.isEmpty()) {
hash.putInt(0)
return

View File

@@ -1,8 +1,8 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental;
import com.dynatrace.hash4j.hashing.HashSink;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.Hashing;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.SmartList;
import com.intellij.util.containers.FileCollectionFactory;
@@ -11,10 +11,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.api.GlobalOptions;
import org.jetbrains.jps.builders.BuildTarget;
import org.jetbrains.jps.builders.BuildTargetRegistry;
import org.jetbrains.jps.builders.ModuleBasedTarget;
import org.jetbrains.jps.builders.TargetOutputIndex;
import org.jetbrains.jps.builders.*;
import org.jetbrains.jps.builders.java.ExcludedJavaSourceRootProvider;
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
@@ -34,7 +31,10 @@ import org.jetbrains.jps.model.module.JpsModuleDependency;
import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot;
import org.jetbrains.jps.service.JpsServiceManager;
import java.io.*;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -49,7 +49,7 @@ import static org.jetbrains.jps.incremental.FileHashUtilKt.normalizedPathHashCod
* Describes a step of compilation process which produces JVM *.class files from files in production/test source roots of a Java module.
* These targets are built by {@link ModuleLevelBuilder} and they are the only targets that can have circular dependencies on each other.
*/
public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRootDescriptor> {
public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRootDescriptor> implements BuildTargetHashSupplier {
private static final Logger LOG = Logger.getInstance(ModuleBuildTarget.class);
public static final Boolean REBUILD_ON_DEPENDENCY_CHANGE = Boolean.valueOf(
@@ -173,17 +173,15 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
}
@Override
public void writeConfiguration(@NotNull ProjectDescriptor pd, @NotNull PrintWriter out) {
public void computeConfigurationDigest(@NotNull ProjectDescriptor projectDescriptor, @NotNull HashSink hash) {
JpsModule module = getModule();
PathRelativizerService relativizer = pd.dataManager.getRelativizer();
PathRelativizerService relativizer = projectDescriptor.dataManager.getRelativizer();
StringBuilder logBuilder = LOG.isDebugEnabled() ? new StringBuilder() : null;
HashStream64 hash = Hashing.komihash5_0().hashStream();
getDependenciesFingerprint(logBuilder, relativizer, hash);
List<JavaSourceRootDescriptor> roots = pd.getBuildRootIndex().getTargetRoots(this, null);
List<JavaSourceRootDescriptor> roots = projectDescriptor.getBuildRootIndex().getTargetRoots(this, null);
for (JavaSourceRootDescriptor root : roots) {
String path = relativizer.toRelative(root.rootFile.toString());
if (logBuilder != null) {
@@ -216,7 +214,7 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
hash.putString(bytecodeTarget);
}
CompilerEncodingConfiguration encodingConfig = pd.getEncodingConfiguration();
CompilerEncodingConfiguration encodingConfig = projectDescriptor.getEncodingConfiguration();
String encoding = encodingConfig.getPreferredModuleEncoding(module);
if (encoding == null) {
hash.putInt(0);
@@ -228,13 +226,11 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
hash.putString(encoding);
}
String hashString = Long.toUnsignedString(hash.getAsLong(), Character.MAX_RADIX);
out.write(hashString);
if (logBuilder == null) {
return;
}
Path configurationTextFile = pd.getTargetsState().getDataPaths().getTargetDataRoot(this).toPath().resolve("config.dat.debug.txt");
Path configurationTextFile = projectDescriptor.getTargetsState().getDataPaths().getTargetDataRoot(this).toPath().resolve("config.dat.debug.txt");
@NonNls String oldText;
try {
oldText = Files.readString(configurationTextFile);
@@ -244,9 +240,9 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
}
String newText = logBuilder.toString();
if (!newText.equals(oldText)) {
if (oldText != null) {
if (oldText != null && hash instanceof HashStream64) {
LOG.debug("Configuration differs from the last recorded one for " + getPresentableName() + ".\nRecorded configuration:\n" + oldText +
"\nCurrent configuration (hash=" + hashString + "):\n" + newText);
"\nCurrent configuration (hash=" + ((HashStream64)hash).getAsLong() + "):\n" + newText);
}
try {
Files.createDirectories(configurationTextFile.getParent());
@@ -260,7 +256,7 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
private void getDependenciesFingerprint(@Nullable StringBuilder logBuilder,
@NotNull PathRelativizerService relativizer,
@NotNull HashStream64 hash) {
@NotNull HashSink hash) {
if (!REBUILD_ON_DEPENDENCY_CHANGE) {
hash.putInt(0);
return;
@@ -282,7 +278,9 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
if (logBuilder != null) {
logBuilder.append(path);
// not a content hash, but the current hash value
logBuilder.append(": ").append(hash.getAsLong());
if (hash instanceof HashStream64) {
logBuilder.append(": ").append((((HashStream64)hash).getAsLong()));
}
logBuilder.append("\n");
}
normalizedPathHashCode(path, hash);
@@ -290,7 +288,7 @@ public final class ModuleBuildTarget extends JVMModuleBuildTarget<JavaSourceRoot
hash.putInt(roots.size());
}
private static void getContentHash(Path file, HashStream64 hash) {
private static void getContentHash(Path file, HashSink hash) {
if (!ProjectStamps.TRACK_LIBRARY_CONTENT) {
hash.putInt(0);
return;

View File

@@ -1,11 +1,11 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.Hashing;
import com.dynatrace.hash4j.hashing.HashSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.builders.BuildTarget;
import org.jetbrains.jps.builders.BuildTargetHashSupplier;
import org.jetbrains.jps.builders.BuildTargetRegistry;
import org.jetbrains.jps.builders.TargetOutputIndex;
import org.jetbrains.jps.builders.java.ExcludedJavaSourceRootProvider;
@@ -26,7 +26,6 @@ import org.jetbrains.jps.service.JpsServiceManager;
import java.io.File;
import java.io.FileFilter;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -37,7 +36,7 @@ import static org.jetbrains.jps.incremental.FileHashUtilKt.normalizedPathHashCod
/**
* Describes a step of compilation process which copies resource's files from source and resource roots of a Java module.
*/
public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDescriptor> {
public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDescriptor> implements BuildTargetHashSupplier {
private final @NotNull ResourcesTargetType targetType;
public ResourcesTarget(@NotNull JpsModule module, @NotNull ResourcesTargetType targetType) {
@@ -115,8 +114,7 @@ public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDesc
}
@Override
public void writeConfiguration(@NotNull ProjectDescriptor projectDescriptor, @NotNull PrintWriter out) {
HashStream64 hash = Hashing.komihash5_0().hashStream();
public void computeConfigurationDigest(@NotNull ProjectDescriptor projectDescriptor, @NotNull HashSink hash) {
PathRelativizerService relativizer = projectDescriptor.dataManager.getRelativizer();
List<ResourceRootDescriptor> roots = projectDescriptor.getBuildRootIndex().getTargetRoots(this, null);
@@ -126,7 +124,5 @@ public final class ResourcesTarget extends JVMModuleBuildTarget<ResourceRootDesc
hash.putString(root.getPackagePrefix());
}
hash.putInt(roots.size());
out.write(Long.toUnsignedString(hash.getAsLong(), Character.MAX_RADIX));
}
}

View File

@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental.artifacts;
import org.jetbrains.annotations.NotNull;
@@ -21,22 +7,22 @@ import org.jetbrains.jps.builders.BuildTargetType;
import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactRootDescriptor;
import org.jetbrains.jps.model.artifact.JpsArtifact;
public abstract class ArtifactBasedBuildTarget extends BuildTarget<ArtifactRootDescriptor> {
private final JpsArtifact myArtifact;
private final JpsArtifact artifact;
protected ArtifactBasedBuildTarget(@NotNull BuildTargetType<? extends BuildTarget<ArtifactRootDescriptor>> targetType, @NotNull JpsArtifact artifact) {
protected ArtifactBasedBuildTarget(@NotNull BuildTargetType<? extends BuildTarget<ArtifactRootDescriptor>> targetType,
@NotNull JpsArtifact artifact) {
super(targetType);
myArtifact = artifact;
this.artifact = artifact;
}
@Override
public @NotNull String getId() {
return myArtifact.getName();
return artifact.getName();
}
public JpsArtifact getArtifact() {
return myArtifact;
return artifact;
}
@Override
@@ -44,11 +30,11 @@ public abstract class ArtifactBasedBuildTarget extends BuildTarget<ArtifactRootD
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return myArtifact.equals(((ArtifactBasedBuildTarget)o).myArtifact);
return artifact.equals(((ArtifactBasedBuildTarget)o).artifact);
}
@Override
public int hashCode() {
return myArtifact.hashCode();
return artifact.hashCode();
}
}

View File

@@ -1,8 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental.artifacts;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.Hashing;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.Strings;
import org.jetbrains.annotations.ApiStatus;
@@ -22,11 +21,10 @@ import org.jetbrains.jps.model.artifact.JpsArtifact;
import org.jetbrains.jps.model.artifact.elements.JpsArtifactOutputPackagingElement;
import java.io.File;
import java.io.PrintWriter;
import java.util.*;
@ApiStatus.Internal
public final class ArtifactBuildTarget extends ArtifactBasedBuildTarget {
public final class ArtifactBuildTarget extends ArtifactBasedBuildTarget implements BuildTargetHashSupplier {
public ArtifactBuildTarget(@NotNull JpsArtifact artifact) {
super(ArtifactBuildTargetType.INSTANCE, artifact);
}
@@ -62,18 +60,16 @@ public final class ArtifactBuildTarget extends ArtifactBasedBuildTarget {
}
@Override
public void writeConfiguration(@NotNull ProjectDescriptor pd, @NotNull PrintWriter out) {
PathRelativizerService relativizer = pd.dataManager.getRelativizer();
public void computeConfigurationDigest(@NotNull ProjectDescriptor projectDescriptor, @NotNull HashSink hash) {
PathRelativizerService relativizer = projectDescriptor.dataManager.getRelativizer();
String outputPath = getArtifact().getOutputPath();
HashStream64 hash = Hashing.komihash5_0().hashStream();
hash.putString(Strings.isEmpty(outputPath) ? "" : relativizer.toRelative(outputPath));
BuildRootIndex rootIndex = pd.getBuildRootIndex();
BuildRootIndex rootIndex = projectDescriptor.getBuildRootIndex();
List<ArtifactRootDescriptor> targetRoots = rootIndex.getTargetRoots(this, null);
for (ArtifactRootDescriptor descriptor : targetRoots) {
descriptor.writeConfiguration(hash, relativizer);
}
hash.putInt(targetRoots.size());
out.write(Long.toUnsignedString(hash.getAsLong(), Character.MAX_RADIX));
}
@Override

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.HashSink;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.builders.BuildOutputConsumer;
@@ -43,7 +43,7 @@ public abstract class ArtifactRootDescriptor extends BuildRootDescriptor {
protected abstract String getFullPath();
public void writeConfiguration(@NotNull HashStream64 hash, PathRelativizerService relativizer) {
public void writeConfiguration(@NotNull HashSink hash, PathRelativizerService relativizer) {
hash.putString(relativizer.toRelative(getFullPath()));
hash.putString(relativizer.toRelative(myDestinationInfo.getOutputPath()));
}

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileFilters;
import com.intellij.openapi.util.io.FileUtil;
@@ -49,7 +49,7 @@ public final class FileBasedArtifactRootDescriptor extends ArtifactRootDescripto
}
@Override
public void writeConfiguration(@NotNull HashStream64 hash, PathRelativizerService relativizer) {
public void writeConfiguration(@NotNull HashSink hash, PathRelativizerService relativizer) {
super.writeConfiguration(hash, relativizer);
myCopyingHandler.writeConfiguration(hash);
}

View File

@@ -1,17 +1,15 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.openapi.util.io.FileFilters;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.cmdline.ProjectDescriptor;
import org.jetbrains.jps.incremental.CompileContext;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @see ArtifactRootCopyingHandlerProvider
@@ -21,12 +19,12 @@ public abstract class FileCopyingHandler {
public abstract void copyFile(@NotNull File from, @NotNull File to, @NotNull CompileContext context) throws IOException;
/**
* Write configuration on which this handler depends. If the output produced by this method changes, the corresponding artifact will be rebuilt
* from the scratch.
* Write configuration on which this handler depends.
* If the output produced by this method changes, the corresponding artifact will be rebuilt from scratch.
*
* @see org.jetbrains.jps.builders.BuildTarget#writeConfiguration(ProjectDescriptor, PrintWriter)
* @see org.jetbrains.jps.builders.BuildTargetHashSupplier
*/
public abstract void writeConfiguration(@NotNull HashStream64 hash);
public abstract void writeConfiguration(@NotNull HashSink hash);
public @NotNull FileFilter createFileFilter() {
return FileFilters.EVERYTHING;

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.openapi.util.io.FileFilters;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -28,7 +28,7 @@ public class FilterCopyHandler extends FileCopyingHandler {
}
@Override
public void writeConfiguration(@NotNull HashStream64 hash) { }
public void writeConfiguration(@NotNull HashSink hash) { }
@Override
public @NotNull FileFilter createFileFilter() {

View File

@@ -1,16 +1,18 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.incremental.storage;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.Hashing;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
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.SmartList;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FileCollectionFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.builders.BuildTarget;
import org.jetbrains.jps.builders.BuildTargetHashSupplier;
import org.jetbrains.jps.cmdline.ProjectDescriptor;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.GlobalContextKey;
@@ -18,7 +20,13 @@ import org.jetbrains.jps.incremental.ModuleBuildTarget;
import org.jetbrains.jps.incremental.relativizer.PathRelativizerService;
import org.jetbrains.jps.model.module.JpsModule;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.*;
public final class BuildTargetConfiguration {
@@ -28,66 +36,67 @@ public final class BuildTargetConfiguration {
private static final GlobalContextKey<Set<File>> ALL_DELETED_ROOTS_KEY = GlobalContextKey.create("_all_deleted_output_roots_");
private static final String DIRTY_MARK = "$dirty_mark$";
private final BuildTarget<?> myTarget;
private final BuildTarget<?> target;
private final BuildTargetsState myTargetsState;
private String myConfiguration;
private volatile String myCurrentState;
private @NotNull String configuration;
private volatile String currentState;
public BuildTargetConfiguration(BuildTarget<?> target, BuildTargetsState targetsState) {
myTarget = target;
this.target = target;
myTargetsState = targetsState;
myConfiguration = load();
configuration = load();
}
private String load() {
File configFile = getConfigFile();
if (configFile.exists()) {
try {
return new String(FileUtil.loadFileText(configFile));
}
catch (IOException e) {
LOG.info("Cannot load configuration of " + myTarget);
}
private @NotNull String load() {
try {
return Files.readString(getConfigFile());
}
catch (NoSuchFileException ignore) {
}
catch (IOException e) {
LOG.info("Cannot load configuration of " + target);
}
return "";
}
public boolean isTargetDirty(@NotNull ProjectDescriptor pd) {
return DIRTY_MARK.equals(myConfiguration) || !getCurrentState(pd).equals(myConfiguration);
public boolean isTargetDirty(@NotNull ProjectDescriptor projectDescriptor) {
return DIRTY_MARK.equals(configuration) || !getCurrentState(projectDescriptor).equals(configuration);
}
public void logDiagnostics(CompileContext context) {
if (DIRTY_MARK.equals(myConfiguration)) {
if (DIRTY_MARK.equals(configuration)) {
if (LOG.isDebugEnabled()) {
LOG.debug(myTarget + " has been marked dirty in the previous compilation session");
LOG.debug(target + " has been marked dirty in the previous compilation session");
}
}
else {
final String currentState = getCurrentState(context.getProjectDescriptor());
if (!currentState.equals(myConfiguration)) {
if (LOG.isDebugEnabled()) {
LOG.debug(myTarget + " configuration was changed:");
LOG.debug("Old:");
LOG.debug(myConfiguration);
LOG.debug("New:");
LOG.debug(currentState);
LOG.debug(myTarget + " will be recompiled");
}
if (myTarget instanceof ModuleBuildTarget) {
final JpsModule module = ((ModuleBuildTarget)myTarget).getModule();
synchronized (MODULES_WITH_TARGET_CONFIG_CHANGED_KEY) {
Set<JpsModule> modules = MODULES_WITH_TARGET_CONFIG_CHANGED_KEY.get(context);
if (modules == null) {
MODULES_WITH_TARGET_CONFIG_CHANGED_KEY.set(context, modules = new HashSet<>());
}
modules.add(module);
String currentState = getCurrentState(context.getProjectDescriptor());
if (currentState.equals(configuration)) {
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug(target + " configuration was changed:");
LOG.debug("Old:");
LOG.debug(configuration);
LOG.debug("New:");
LOG.debug(currentState);
LOG.debug(target + " will be recompiled");
}
if (target instanceof ModuleBuildTarget) {
final JpsModule module = ((ModuleBuildTarget)target).getModule();
synchronized (MODULES_WITH_TARGET_CONFIG_CHANGED_KEY) {
Set<JpsModule> modules = MODULES_WITH_TARGET_CONFIG_CHANGED_KEY.get(context);
if (modules == null) {
MODULES_WITH_TARGET_CONFIG_CHANGED_KEY.set(context, modules = new HashSet<>());
}
modules.add(module);
}
}
}
}
public void save(CompileContext context) {
public void save(@NotNull CompileContext context) {
persist(getCurrentState(context.getProjectDescriptor()));
}
@@ -95,64 +104,70 @@ public final class BuildTargetConfiguration {
persist(DIRTY_MARK);
}
private void persist(final String data) {
private void persist(@NotNull String data) {
try {
File configFile = getConfigFile();
FileUtil.createParentDirs(configFile);
try (Writer out = new BufferedWriter(new FileWriter(configFile))) {
out.write(data);
myConfiguration = data;
}
Path configFile = getConfigFile();
Files.createDirectories(configFile.getParent());
Files.writeString(configFile, data);
configuration = data;
}
catch (IOException e) {
LOG.info("Cannot save configuration of " + myConfiguration, e);
LOG.info("Cannot save configuration of " + configuration, e);
}
}
private File getConfigFile() {
return new File(myTargetsState.getDataPaths().getTargetDataRoot(myTarget), "config.dat");
private Path getConfigFile() {
return myTargetsState.getDataPaths().getTargetDataRoot(target).toPath().resolve("config.dat");
}
private File getNonexistentOutputsFile() {
return new File(myTargetsState.getDataPaths().getTargetDataRoot(myTarget), "nonexistent-outputs.dat");
private Path getNonexistentOutputsFile() {
return myTargetsState.getDataPaths().getTargetDataRoot(target).toPath().resolve("nonexistent-outputs.dat");
}
private @NotNull String getCurrentState(@NotNull ProjectDescriptor pd) {
String state = myCurrentState;
if (state == null) {
myCurrentState = state = saveToString(pd);
String state = currentState;
if (state != null) {
return state;
}
return state;
}
private @NotNull String saveToString(@NotNull ProjectDescriptor pd) {
StringWriter out = new StringWriter();
myTarget.writeConfiguration(pd, new PrintWriter(out));
return out.toString();
if (target instanceof BuildTargetHashSupplier) {
HashStream64 hash = Hashing.komihash5_0().hashStream();
((BuildTargetHashSupplier)target).computeConfigurationDigest(pd, hash);
state = Long.toUnsignedString(hash.getAsLong(), Character.MAX_RADIX);
}
else {
StringWriter out = new StringWriter();
target.writeConfiguration(pd, new PrintWriter(out));
state = out.toString();
}
currentState = state;
return state;
}
public void storeNonexistentOutputRoots(CompileContext context) throws IOException {
PathRelativizerService relativizer = context.getProjectDescriptor().dataManager.getRelativizer();
Collection<File> outputRoots = myTarget.getOutputRoots(context);
List<String> nonexistentOutputRoots = new SmartList<>();
Collection<File> outputRoots = target.getOutputRoots(context);
List<String> nonexistentOutputRoots = new ArrayList<>();
for (File root : outputRoots) {
if (!root.exists()) {
nonexistentOutputRoots.add(relativizer.toRelative(root.getAbsolutePath()));
}
}
File file = getNonexistentOutputsFile();
Path file = getNonexistentOutputsFile();
if (nonexistentOutputRoots.isEmpty()) {
file.delete();
Files.deleteIfExists(file);
}
else {
FileUtil.writeToFile(file, StringUtil.join(nonexistentOutputRoots, "\n"));
Files.createDirectories(file.getParent());
Files.writeString(file, String.join("\n", nonexistentOutputRoots));
}
}
public boolean outputRootWasDeleted(CompileContext context) throws IOException {
List<String> nonexistentOutputRoots = new SmartList<>();
List<String> nonexistentOutputRoots = new ArrayList<>();
final Collection<File> targetRoots = myTarget.getOutputRoots(context);
final Collection<File> targetRoots = target.getOutputRoots(context);
synchronized (ALL_DELETED_ROOTS_KEY) {
Set<File> allDeletedRoots = ALL_DELETED_ROOTS_KEY.get(context);
for (File outputRoot : targetRoots) {
@@ -168,7 +183,7 @@ public final class BuildTargetConfiguration {
}
}
if (wasDeleted) {
nonexistentOutputRoots.add(FileUtil.toSystemIndependentName(outputRoot.getAbsolutePath()));
nonexistentOutputRoots.add(FileUtilRt.toSystemIndependentName(outputRoot.getAbsolutePath()));
}
}
}
@@ -178,14 +193,13 @@ public final class BuildTargetConfiguration {
}
Set<String> storedNonExistentOutputs;
File file = getNonexistentOutputsFile();
if (!file.exists()) {
storedNonExistentOutputs = Collections.emptySet();
Path file = getNonexistentOutputsFile();
if (Files.notExists(file)) {
storedNonExistentOutputs = Set.of();
}
else {
PathRelativizerService relativizer = context.getProjectDescriptor().dataManager.getRelativizer();
List<String> lines = ContainerUtil.map(StringUtil.split(FileUtil.loadFile(file), "\n"),
s -> relativizer.toFull(s));
List<String> lines = ContainerUtil.map(StringUtil.split(Files.readString(file), "\n"), s -> relativizer.toFull(s));
storedNonExistentOutputs = CollectionFactory.createFilePathSet(lines);
}
return !storedNonExistentOutputs.containsAll(nonexistentOutputRoots);

View File

@@ -334,7 +334,8 @@ public final class BuildTargetSourcesState implements BuildListener {
private static long getOutputFileHash(@NotNull Path file, @NotNull Path rootPath, @NotNull HashStream64 hashToReuse) throws IOException {
// reduce GC - reuse hashToReuse - do not inline fileHash variable
long fileHash = FileHashUtilKt.getFileHash(file, hashToReuse);
FileHashUtilKt.getFileHash(file, hashToReuse.reset());
long fileHash = hashToReuse.getAsLong();
return hashToReuse
.reset()
.putLong(fileHash)

View File

@@ -3,8 +3,7 @@
package com.intellij.devkit.runtimeModuleRepository.jps.build
import com.dynatrace.hash4j.hashing.HashStream64
import com.dynatrace.hash4j.hashing.Hashing
import com.dynatrace.hash4j.hashing.HashSink
import com.intellij.devkit.runtimeModuleRepository.jps.build.RuntimeModuleRepositoryBuildConstants.JAR_REPOSITORY_FILE_NAME
import com.intellij.devkit.runtimeModuleRepository.jps.impl.DevkitRuntimeModuleRepositoryJpsBundle
import com.intellij.openapi.diagnostic.logger
@@ -26,10 +25,11 @@ import org.jetbrains.jps.model.module.JpsModuleReference
import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService
import org.jetbrains.jps.util.JpsPathUtil
import java.io.File
import java.io.PrintWriter
import kotlin.system.measureTimeMillis
internal class RuntimeModuleRepositoryTarget(val project: JpsProject) : BuildTarget<BuildRootDescriptor?>(RuntimeModuleRepositoryTarget) {
internal class RuntimeModuleRepositoryTarget(
val project: JpsProject,
) : BuildTarget<BuildRootDescriptor>(RuntimeModuleRepositoryTarget), BuildTargetHashSupplier {
override fun getId(): String {
return "project"
}
@@ -72,54 +72,52 @@ internal class RuntimeModuleRepositoryTarget(val project: JpsProject) : BuildTar
return java.util.List.of(File(JpsPathUtil.urlToFile(outputUrl), JAR_REPOSITORY_FILE_NAME))
}
override fun writeConfiguration(pd: ProjectDescriptor, out: PrintWriter) {
val digest: Long
override fun computeConfigurationDigest(projectDescriptor: ProjectDescriptor, hash: HashSink) {
hash.putString(JarFileSerializer.SPECIFICATION_VERSION)
hash.putInt(RuntimeModuleRepositoryBuildConstants.GENERATOR_VERSION)
val time = measureTimeMillis {
digest = computeDependenciesDigest(pd)
computeDependenciesDigest(projectDescriptor, hash)
}
LOG.info("Dependencies digest computed in ${time}ms")
out.println("${JarFileSerializer.SPECIFICATION_VERSION}.${RuntimeModuleRepositoryBuildConstants.GENERATOR_VERSION}")
out.println(java.lang.Long.toUnsignedString(digest, Character.MAX_RADIX))
}
private fun computeDependenciesDigest(pd: ProjectDescriptor): Long {
val digest = Hashing.komihash5_0().hashStream()
private fun computeDependenciesDigest(pd: ProjectDescriptor, hash: HashSink) {
val relativizer = pd.dataManager.relativizer
val modules = pd.project.modules
for (module in modules) {
digest.putString(module.name)
hash.putString(module.name)
val sourceRoots = module.sourceRoots
for (sourceRoot in sourceRoots) {
digest.putString(relativizer.toRelative(sourceRoot.path.toString()))
hash.putString(relativizer.toRelative(sourceRoot.path.toString()))
}
digest.putInt(sourceRoots.size)
hash.putInt(sourceRoots.size)
var counter = 0
RuntimeModuleRepositoryBuilder.enumerateRuntimeDependencies(module).processModuleAndLibraries(
{
digest.putString(it.name)
hash.putString(it.name)
counter++
},
{ library ->
digest.putString(library.name)
hash.putString(library.name)
if (library.createReference().parentReference is JpsModuleReference) {
updateFromRoots(library, digest, relativizer)
updateFromRoots(library, hash, relativizer)
}
counter++
},
)
digest.putInt(counter)
hash.putInt(counter)
}
digest.putInt(modules.size)
hash.putInt(modules.size)
val libraries = pd.project.libraryCollection.libraries
for (library in libraries) {
digest.putString(library.name)
updateFromRoots(library, digest, relativizer)
hash.putString(library.name)
updateFromRoots(library, hash, relativizer)
}
digest.putInt(libraries.size)
return digest.asLong
hash.putInt(libraries.size)
}
companion object : BuildTargetType<RuntimeModuleRepositoryTarget?>(RuntimeModuleRepositoryBuildConstants.TARGET_TYPE_ID), ModuleInducedTargetType {
@@ -152,7 +150,7 @@ internal class RuntimeModuleRepositoryTarget(val project: JpsProject) : BuildTar
return project.modules.any { it.name == "intellij.idea.community.main" || it.name == "intellij.platform.commercial" }
}
private fun updateFromRoots(library: JpsLibrary, digest: HashStream64, relativizer: PathRelativizerService) {
private fun updateFromRoots(library: JpsLibrary, digest: HashSink, relativizer: PathRelativizerService) {
val roots = library.getRoots(JpsOrderRootType.COMPILED)
for (root in roots) {
digest.putString(relativizer.toRelative(JpsPathUtil.urlToPath(root.url)))

View File

@@ -48,5 +48,7 @@
<orderEntry type="library" name="opentelemetry" level="project" />
<orderEntry type="library" name="opentelemetry-semconv" level="project" />
<orderEntry type="module" module-name="intellij.platform.backend.observation" />
<orderEntry type="library" name="hash4j" level="project" />
<orderEntry type="library" name="fastutil-min" level="project" />
</component>
</module>

View File

@@ -73,7 +73,6 @@
<compiler.updateResourcesBuildContributor
implementation="org.jetbrains.plugins.gradle.execution.build.GradleUpdateResourcesBuildContributor"/>
<projectService serviceImplementation="org.jetbrains.plugins.gradle.config.GradleResourceCompilerConfigurationGenerator"/>
<compiler.task execute="BEFORE" implementation="org.jetbrains.plugins.gradle.config.GradleResourceConfigurationGeneratorCompileTask"/>
<attachSourcesProvider implementation="org.jetbrains.plugins.gradle.action.GradleAttachSourcesProvider"/>

View File

@@ -1,11 +1,14 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.gradle.config;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.Hashing;
import com.intellij.compiler.server.BuildManager;
import com.intellij.facet.Facet;
import com.intellij.facet.FacetManager;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemExecutionAware;
@@ -28,6 +31,7 @@ import com.intellij.util.PathMapper;
import com.intellij.util.PlatformUtils;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.xmlb.XmlSerializer;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -45,27 +49,24 @@ import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Vladislav.Soroka
*/
public class GradleResourceCompilerConfigurationGenerator {
@SuppressWarnings("SSBasedInspection")
@Service(Service.Level.PROJECT)
public final class GradleResourceCompilerConfigurationGenerator {
private static final Logger LOG = Logger.getInstance(GradleResourceCompilerConfigurationGenerator.class);
private final @NotNull Project myProject;
private final @NotNull Map<String, Integer> myModulesConfigurationHash;
private final @NotNull Map<String, Long> modulesConfigurationHash = new ConcurrentHashMap<>();
private final ExternalProjectDataCache externalProjectDataCache;
public GradleResourceCompilerConfigurationGenerator(final @NotNull Project project) {
myProject = project;
myModulesConfigurationHash = new ConcurrentHashMap<>();
externalProjectDataCache = ExternalProjectDataCache.getInstance(project);
assert externalProjectDataCache != null;
project.getMessageBus().connect().subscribe(ModuleListener.TOPIC, new ModuleListener() {
@Override
public void moduleRemoved(@NotNull Project project, @NotNull Module module) {
myModulesConfigurationHash.remove(module.getName());
modulesConfigurationHash.remove(module.getName());
}
@Override
@@ -79,40 +80,40 @@ public class GradleResourceCompilerConfigurationGenerator {
});
}
public void generateBuildConfiguration(final @NotNull CompileContext context) {
public void generateBuildConfiguration(@NotNull CompileContext context) {
if (shouldBeBuiltByExternalSystem(myProject) || !hasGradleModules(context)) {
return;
}
if (shouldBeBuiltByExternalSystem(myProject)) return;
BuildManager buildManager = BuildManager.getInstance();
File projectSystemDir = buildManager.getProjectSystemDirectory(myProject);
if (!hasGradleModules(context)) return;
File gradleConfigFile = new File(projectSystemDir, GradleProjectConfiguration.CONFIGURATION_FILE_RELATIVE_PATH);
final BuildManager buildManager = BuildManager.getInstance();
final File projectSystemDir = buildManager.getProjectSystemDirectory(myProject);
final File gradleConfigFile = new File(projectSystemDir, GradleProjectConfiguration.CONFIGURATION_FILE_RELATIVE_PATH);
final Map<String, GradleModuleResourceConfiguration> affectedGradleModuleConfigurations =
generateAffectedGradleModulesConfiguration(context);
Map<String, GradleModuleResourceConfiguration> affectedGradleModuleConfigurations = generateAffectedGradleModulesConfiguration(context);
if (affectedGradleModuleConfigurations.isEmpty()) return;
boolean configurationUpdateRequired = context.isRebuild() || !gradleConfigFile.exists();
final Map<String, Integer> affectedConfigurationHash = new HashMap<>();
Object2LongOpenHashMap<String> affectedConfigurationHash = new Object2LongOpenHashMap<>();
for (Map.Entry<String, GradleModuleResourceConfiguration> entry : affectedGradleModuleConfigurations.entrySet()) {
Integer moduleLastConfigurationHash = myModulesConfigurationHash.get(entry.getKey());
int moduleCurrentConfigurationHash = entry.getValue().computeConfigurationHash();
if (moduleLastConfigurationHash == null || moduleLastConfigurationHash.intValue() != moduleCurrentConfigurationHash) {
Long moduleLastConfigurationHash = modulesConfigurationHash.get(entry.getKey());
HashStream64 hash = Hashing.komihash5_0().hashStream();
entry.getValue().computeConfigurationHash(hash);
long moduleCurrentConfigurationHash = hash.getAsLong();
if (moduleLastConfigurationHash == null || moduleLastConfigurationHash.longValue() != moduleCurrentConfigurationHash) {
configurationUpdateRequired = true;
}
affectedConfigurationHash.put(entry.getKey(), moduleCurrentConfigurationHash);
}
final GradleProjectConfiguration projectConfig = loadLastConfiguration(gradleConfigFile);
GradleProjectConfiguration projectConfig = loadLastConfiguration(gradleConfigFile);
projectConfig.moduleConfigurations.putAll(affectedGradleModuleConfigurations);
final Element element = new Element("gradle-project-configuration");
Element element = new Element("gradle-project-configuration");
XmlSerializer.serializeInto(projectConfig, element);
final boolean finalConfigurationUpdateRequired = configurationUpdateRequired;
boolean finalConfigurationUpdateRequired = configurationUpdateRequired;
buildManager.runCommand(() -> {
if (finalConfigurationUpdateRequired) {
buildManager.clearState(myProject);
@@ -120,7 +121,7 @@ public class GradleResourceCompilerConfigurationGenerator {
FileUtil.createIfDoesntExist(gradleConfigFile);
try {
JDOMUtil.write(element, gradleConfigFile.toPath());
myModulesConfigurationHash.putAll(affectedConfigurationHash);
modulesConfigurationHash.putAll(affectedConfigurationHash);
}
catch (IOException e) {
throw new RuntimeException(e);
@@ -129,13 +130,13 @@ public class GradleResourceCompilerConfigurationGenerator {
}
private @NotNull GradleProjectConfiguration loadLastConfiguration(@NotNull File gradleConfigFile) {
final GradleProjectConfiguration projectConfig = new GradleProjectConfiguration();
GradleProjectConfiguration projectConfig = new GradleProjectConfiguration();
if (gradleConfigFile.exists()) {
try {
XmlSerializer.deserializeInto(projectConfig, JDOMUtil.load(gradleConfigFile));
// filter orphan modules
final Set<String> actualModules = myModulesConfigurationHash.keySet();
Set<String> actualModules = modulesConfigurationHash.keySet();
for (Iterator<Map.Entry<String, GradleModuleResourceConfiguration>> iterator =
projectConfig.moduleConfigurations.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<String, GradleModuleResourceConfiguration> configurationEntry = iterator.next();

View File

@@ -38,5 +38,6 @@
<orderEntry type="library" name="fastutil-min" level="project" />
<orderEntry type="module" module-name="intellij.platform.util.jdom" />
<orderEntry type="module" module-name="intellij.platform.jps.build.tests" scope="TEST" />
<orderEntry type="library" name="hash4j" level="project" />
</component>
</module>

View File

@@ -1,8 +1,7 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.gradle.model.impl;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.annotations.OptionTag;
import com.intellij.util.xmlb.annotations.Tag;
@@ -17,14 +16,10 @@ import java.util.List;
/**
* @author Vladislav.Soroka
*/
public class GradleModuleResourceConfiguration {
@NotNull
@Tag("id")
public ModuleVersion id;
public final class GradleModuleResourceConfiguration {
@Tag("id") public @NotNull ModuleVersion id;
@Nullable
@Tag("parentId")
public ModuleVersion parentId;
@Tag("parentId") public @Nullable ModuleVersion parentId;
@OptionTag
public boolean overwrite;
@@ -38,34 +33,42 @@ public class GradleModuleResourceConfiguration {
@XCollection(propertyElementName = "test-resources", elementName = "resource")
public List<ResourceRootConfiguration> testResources = new ArrayList<>();
public int computeConfigurationHash(boolean forTestResources, PathRelativizerService pathRelativizerService) {
int result = computeModuleConfigurationHash();
public void computeConfigurationHash(boolean forTestResources, PathRelativizerService pathRelativizerService, @NotNull HashSink hash) {
computeModuleConfigurationHash(hash);
final List<ResourceRootConfiguration> _resources = forTestResources ? testResources : resources;
result = 31 * result;
List<ResourceRootConfiguration> _resources = forTestResources ? testResources : resources;
for (ResourceRootConfiguration resource : _resources) {
result += resource.computeConfigurationHash(pathRelativizerService);
resource.computeConfigurationHash(pathRelativizerService, hash);
}
return result;
hash.putInt(_resources.size());
}
public int computeConfigurationHash() {
int result = computeModuleConfigurationHash();
public void computeConfigurationHash(@NotNull HashSink hash) {
computeModuleConfigurationHash(hash);
final List<ResourceRootConfiguration> _resources = ContainerUtil.concat(testResources, resources);
result = 31 * result;
List<ResourceRootConfiguration> _resources = ContainerUtil.concat(testResources, resources);
for (ResourceRootConfiguration resource : _resources) {
result += resource.computeConfigurationHash(null);
resource.computeConfigurationHash(null, hash);
}
return result;
hash.putInt(_resources.size());
}
public int computeModuleConfigurationHash() {
int result = id.hashCode();
result = 31 * result + (parentId != null ? parentId.hashCode() : 0);
result = 31 * result + (outputDirectory != null ? outputDirectory.hashCode() : 0);
result = 31 * result + (overwrite ? 1 : 0);
return result;
public void computeModuleConfigurationHash(@NotNull HashSink hash) {
hash.putInt(id.hashCode());
if (parentId == null) {
hash.putBoolean(false);
}
else {
hash.putBoolean(true);
parentId.computeHash(hash);
}
if (outputDirectory == null) {
hash.putInt(-1);
}
else {
hash.putString(outputDirectory);
}
hash.putBoolean(overwrite);
}
}

View File

@@ -1,8 +1,8 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.gradle.model.impl;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.FileCollectionFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -20,13 +20,12 @@ import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.util.JpsPathUtil;
import java.io.File;
import java.io.PrintWriter;
import java.util.*;
/**
* @author Vladislav.Soroka
*/
public final class GradleResourcesTarget extends ModuleBasedTarget<GradleResourceRootDescriptor> {
public final class GradleResourcesTarget extends ModuleBasedTarget<GradleResourceRootDescriptor> implements BuildTargetHashSupplier {
GradleResourcesTarget(@NotNull GradleResourcesTargetType type, @NotNull JpsModule module) {
super(type, module);
}
@@ -46,9 +45,8 @@ public final class GradleResourcesTarget extends ModuleBasedTarget<GradleResourc
return true;
}
@NotNull
@Override
public List<GradleResourceRootDescriptor> computeRootDescriptors(@NotNull JpsModel model, @NotNull ModuleExcludeIndex index, @NotNull IgnoredFileIndex ignoredFileIndex, @NotNull BuildDataPaths dataPaths) {
public @NotNull List<GradleResourceRootDescriptor> computeRootDescriptors(@NotNull JpsModel model, @NotNull ModuleExcludeIndex index, @NotNull IgnoredFileIndex ignoredFileIndex, @NotNull BuildDataPaths dataPaths) {
final List<GradleResourceRootDescriptor> result = new ArrayList<>();
GradleProjectConfiguration projectConfig = JpsGradleExtensionService.getInstance().getGradleProjectConfiguration(dataPaths);
@@ -80,9 +78,8 @@ public final class GradleResourcesTarget extends ModuleBasedTarget<GradleResourc
return ((GradleResourcesTargetType)getTargetType()).isTests();
}
@Nullable
@Override
public GradleResourceRootDescriptor findRootDescriptor(@NotNull String rootId, @NotNull BuildRootIndex rootIndex) {
public @Nullable GradleResourceRootDescriptor findRootDescriptor(@NotNull String rootId, @NotNull BuildRootIndex rootIndex) {
for (GradleResourceRootDescriptor descriptor : rootIndex.getTargetRoots(this, null)) {
if (descriptor.getRootId().equals(rootId)) {
return descriptor;
@@ -91,15 +88,13 @@ public final class GradleResourcesTarget extends ModuleBasedTarget<GradleResourc
return null;
}
@NotNull
@Override
public String getPresentableName() {
public @NotNull String getPresentableName() {
return getTargetType().getTypeId() + ":" + myModule.getName();
}
@NotNull
@Override
public Collection<File> getOutputRoots(@NotNull CompileContext context) {
public @NotNull Collection<File> getOutputRoots(@NotNull CompileContext context) {
GradleModuleResourceConfiguration configuration =
getModuleResourcesConfiguration(context.getProjectDescriptor().dataManager.getDataPaths());
final Set<File> result = FileCollectionFactory.createCanonicalFileSet();
@@ -113,13 +108,11 @@ public final class GradleResourcesTarget extends ModuleBasedTarget<GradleResourc
return result;
}
@Nullable
public File getModuleOutputDir() {
public @Nullable File getModuleOutputDir() {
return JpsJavaExtensionService.getInstance().getOutputDirectory(myModule, isTests());
}
@Nullable
public static File getOutputDir(@Nullable File moduleOutput, ResourceRootConfiguration config, @Nullable String outputDirectory) {
public static @Nullable File getOutputDir(@Nullable File moduleOutput, ResourceRootConfiguration config, @Nullable String outputDirectory) {
if(outputDirectory != null) {
moduleOutput = JpsPathUtil.urlToFile(outputDirectory);
}
@@ -128,21 +121,26 @@ public final class GradleResourcesTarget extends ModuleBasedTarget<GradleResourc
return null;
}
String targetPath = config.targetPath;
if (StringUtil.isEmptyOrSpaces(targetPath)) {
if (targetPath == null || targetPath.isBlank()) {
return moduleOutput;
}
final File targetPathFile = new File(targetPath);
final File outputFile = targetPathFile.isAbsolute() ? targetPathFile : new File(moduleOutput, targetPath);
File targetPathFile = new File(targetPath);
File outputFile = targetPathFile.isAbsolute() ? targetPathFile : new File(moduleOutput, targetPath);
return new File(FileUtil.toCanonicalPath(outputFile.getPath()));
}
@Override
public void writeConfiguration(@NotNull ProjectDescriptor pd, @NotNull PrintWriter out) {
final BuildDataPaths dataPaths = pd.getTargetsState().getDataPaths();
final GradleModuleResourceConfiguration configuration = getModuleResourcesConfiguration(dataPaths);
if (configuration != null) {
PathRelativizerService pathRelativizerService = pd.dataManager.getRelativizer();
out.write(Integer.toHexString(configuration.computeConfigurationHash(isTests(), pathRelativizerService)));
public void computeConfigurationDigest(@NotNull ProjectDescriptor projectDescriptor, @NotNull HashSink hash) {
BuildDataPaths dataPaths = projectDescriptor.getTargetsState().getDataPaths();
GradleModuleResourceConfiguration configuration = getModuleResourcesConfiguration(dataPaths);
if (configuration == null) {
hash.putBoolean(false);
}
else {
hash.putBoolean(true);
PathRelativizerService pathRelativizerService = projectDescriptor.dataManager.getRelativizer();
configuration.computeConfigurationHash(isTests(), pathRelativizerService, hash);
}
}
}

View File

@@ -1,26 +1,15 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.gradle.model.impl;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Vladislav.Soroka
*/
public class ModuleVersion {
public final class ModuleVersion {
@Tag("groupId")
public String groupId;
@@ -60,4 +49,19 @@ public class ModuleVersion {
result = 31 * result + (version != null ? version.hashCode() : 0);
return result;
}
public void computeHash(@NotNull HashSink hash) {
hashNullableString(groupId, hash);
hashNullableString(artifactId, hash);
hashNullableString(version, hash);
}
private static void hashNullableString(@Nullable String s, @NotNull HashSink hash) {
if (s == null) {
hash.putInt(-1);
}
else {
hash.putString(s);
}
}
}

View File

@@ -1,8 +1,9 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.gradle.model.impl;
import com.dynatrace.hash4j.hashing.HashFunnel;
import com.dynatrace.hash4j.hashing.HashSink;
import com.dynatrace.hash4j.hashing.Hashing;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Tag;
import com.intellij.util.xmlb.annotations.XCollection;
@@ -17,14 +18,10 @@ import java.util.List;
* @author Vladislav.Soroka
*/
@Tag("resource")
public class ResourceRootConfiguration extends FilePattern {
@Tag("directory")
@NotNull
public String directory;
public final class ResourceRootConfiguration extends FilePattern {
@Tag("directory") public @NotNull String directory;
@Tag("targetPath")
@Nullable
public String targetPath;
@Tag("targetPath") public @Nullable String targetPath;
@Attribute("filtered")
public boolean isFiltered;
@@ -32,22 +29,33 @@ public class ResourceRootConfiguration extends FilePattern {
@XCollection(propertyElementName = "filters", elementName = "filter")
public List<ResourceRootFilter> filters = new ArrayList<>();
public int computeConfigurationHash(@Nullable PathRelativizerService pathRelativizerService) {
int result;
if(pathRelativizerService == null) {
result = directory.hashCode();
result = 31 * result + (targetPath != null ? targetPath.hashCode() : 0);
} else {
result = pathRelativizerService.toRelative(directory).hashCode();
result = 31 * result + (targetPath != null ? pathRelativizerService.toRelative(targetPath).hashCode() : 0);
public void computeConfigurationHash(@Nullable PathRelativizerService pathRelativizerService, @NotNull HashSink hash) {
if (pathRelativizerService == null) {
hash.putString(directory);
if (targetPath == null) {
hash.putInt(-1);
}
else {
hash.putString(targetPath);
}
}
else {
hash.putString(pathRelativizerService.toRelative(directory));
if (targetPath == null) {
hash.putInt(-1);
}
else {
hash.putString(pathRelativizerService.toRelative(targetPath));
}
}
result = 31 * result + (isFiltered ? 1 : 0);
result = 31 * result + includes.hashCode();
result = 31 * result + excludes.hashCode();
hash.putBoolean(isFiltered);
hash.putUnorderedIterable(includes, HashFunnel.forString(), Hashing.komihash5_0());
hash.putUnorderedIterable(excludes, HashFunnel.forString(), Hashing.komihash5_0());
for (ResourceRootFilter filter : filters) {
result = 31 * result + filter.computeConfigurationHash();
filter.computeConfigurationHash(hash);
}
return result;
hash.putInt(filters.size());
}
}

View File

@@ -1,20 +1,7 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.gradle.model.impl;
import com.dynatrace.hash4j.hashing.HashSink;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
@@ -32,25 +19,18 @@ import java.util.regex.Pattern;
* @author Vladislav.Soroka
*/
@Tag("filter")
public class ResourceRootFilter {
@Tag("filterType")
@NotNull
@NlsSafe
public String filterType;
@Tag("properties")
@NotNull
public String properties;
public final class ResourceRootFilter {
@Tag("filterType") public @NotNull @NlsSafe String filterType;
@Tag("properties") public @NotNull String properties;
private transient Map<Object, Object> propertiesMap;
public int computeConfigurationHash() {
int result = filterType.hashCode();
result = 31 * result + getProperties().hashCode();
return result;
public void computeConfigurationHash(@NotNull HashSink hash) {
hash.putString(filterType);
hash.putString(properties);
}
@NotNull
public Map<Object, Object> getProperties() {
public @NotNull Map<Object, Object> getProperties() {
if (propertiesMap == null) {
try {
Gson gson = new GsonBuilder().create();
@@ -59,16 +39,16 @@ public class ResourceRootFilter {
new TypeToken<Map<Object, Object>>() {
}.getType());
if("RenamingCopyFilter".equals(filterType)) {
if ("RenamingCopyFilter".equals(filterType)) {
final Object pattern = propertiesMap.get("pattern");
final Matcher matcher = Pattern.compile(pattern instanceof String ? (String)pattern : "").matcher("");
propertiesMap.put("matcher", matcher);
}
}
catch (JsonParseException e) {
throw new RuntimeException("Unsupported filter: " + properties , e);
throw new RuntimeException("Unsupported filter: " + properties, e);
}
if(propertiesMap == null) {
if (propertiesMap == null) {
propertiesMap = new HashMap<>();
}
}

View File

@@ -2,7 +2,7 @@
package org.jetbrains.jps.maven.compiler;
import com.dynatrace.hash4j.hashing.HashFunnel;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.HashSink;
import com.dynatrace.hash4j.hashing.Hashing;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileUtil;
@@ -90,7 +90,6 @@ public final class MavenWebArtifactRootCopyingHandlerProvider extends ArtifactRo
}
private static class MavenWebArtifactCopyingHandler extends FilterCopyHandler {
private final ResourceRootConfiguration myWarRootConfig;
private final MavenModuleResourceConfiguration myModuleResourceConfig;
@@ -116,12 +115,12 @@ public final class MavenWebArtifactRootCopyingHandlerProvider extends ArtifactRo
}
@Override
public void writeConfiguration(@NotNull HashStream64 hash) {
public void writeConfiguration(@NotNull HashSink hash) {
hash.putString("maven hash:");
configurationHash(hash);
}
protected void configurationHash(@NotNull HashStream64 hash) {
protected void configurationHash(@NotNull HashSink hash) {
hash.putUnorderedIterable(myWarRootConfig.includes, HashFunnel.forString(), Hashing.komihash5_0());
hash.putUnorderedIterable(myWarRootConfig.excludes, HashFunnel.forString(), Hashing.komihash5_0());
myWarRootConfig.computeConfigurationHash(hash);
@@ -181,7 +180,7 @@ public final class MavenWebArtifactRootCopyingHandlerProvider extends ArtifactRo
}
@Override
protected void configurationHash(@NotNull HashStream64 hash) {
protected void configurationHash(@NotNull HashSink hash) {
super.configurationHash(hash);
FileHashUtilKt.normalizedPathHashCode(myTargetDir.toPath().toAbsolutePath().normalize().toString(), hash);
@@ -246,7 +245,8 @@ public final class MavenWebArtifactRootCopyingHandlerProvider extends ArtifactRo
FileFilter superFileFilter = super.createFileFilter();
FileFilter rootFileFilter = new MavenResourceFileFilter(root, myRootConfiguration).acceptingWebXml();
//for additional resource directory 'exclude' means 'exclude from copying' but for the default webapp resource it mean 'exclude from filtering'
// for additional resource directory 'exclude' means 'exclude from copying'
// but for the default webapp resource it means 'exclude from filtering'
boolean isMainWebAppRoot = FileUtil.pathsEqual(artifactConfiguration.warSourceDirectory, rootConfiguration.directory);
if (isMainWebAppRoot) {
@@ -272,7 +272,7 @@ public final class MavenWebArtifactRootCopyingHandlerProvider extends ArtifactRo
}
@Override
protected void configurationHash(@NotNull HashStream64 hash) {
protected void configurationHash(@NotNull HashSink hash) {
myRootConfiguration.computeConfigurationHash(hash);
super.configurationHash(hash);
}

View File

@@ -2,7 +2,7 @@
package org.jetbrains.jps.maven.model.impl;
import com.dynatrace.hash4j.hashing.HashFunnel;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.HashSink;
import com.dynatrace.hash4j.hashing.Hashing;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.xmlb.annotations.MapAnnotation;
@@ -74,7 +74,7 @@ public final class MavenModuleResourceConfiguration {
return Collections.unmodifiableSet(result);
}
public void computeConfigurationHash(boolean forTestResources, @NotNull HashStream64 hash) {
public void computeConfigurationHash(boolean forTestResources, @NotNull HashSink hash) {
computeModuleConfigurationHash(hash);
List<ResourceRootConfiguration> _resources = forTestResources? testResources : resources;
@@ -84,7 +84,7 @@ public final class MavenModuleResourceConfiguration {
hash.putInt(_resources.size());
}
public void computeModuleConfigurationHash(@NotNull HashStream64 hash) {
public void computeModuleConfigurationHash(@NotNull HashSink hash) {
hash.putInt(parentId == null ? 0 : parentId.hashCode());
hash.putString(directory);
hashNullableString(manifest, hash);
@@ -105,7 +105,7 @@ public final class MavenModuleResourceConfiguration {
hash.putBoolean(overwrite);
}
private static void hashNullableString(@Nullable String s, @NotNull HashStream64 hash) {
private static void hashNullableString(@Nullable String s, @NotNull HashSink hash) {
if (s == null) {
hash.putInt(-1);
}

View File

@@ -1,8 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.maven.model.impl;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.Hashing;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.util.containers.FileCollectionFactory;
import org.jetbrains.annotations.ApiStatus;
@@ -21,14 +20,13 @@ import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.util.JpsPathUtil;
import java.io.File;
import java.io.PrintWriter;
import java.util.*;
/**
* @author Eugene Zhuravlev
*/
@ApiStatus.Internal
public final class MavenResourcesTarget extends ModuleBasedTarget<MavenResourceRootDescriptor> {
public final class MavenResourcesTarget extends ModuleBasedTarget<MavenResourceRootDescriptor> implements BuildTargetHashSupplier {
MavenResourcesTarget(@NotNull MavenResourcesTargetType type, @NotNull JpsModule module) {
super(type, module);
}
@@ -137,13 +135,15 @@ public final class MavenResourcesTarget extends ModuleBasedTarget<MavenResourceR
}
@Override
public void writeConfiguration(@NotNull ProjectDescriptor pd, @NotNull PrintWriter out) {
BuildDataPaths dataPaths = pd.getTargetsState().getDataPaths();
public void computeConfigurationDigest(@NotNull ProjectDescriptor projectDescriptor, @NotNull HashSink hash) {
BuildDataPaths dataPaths = projectDescriptor.getTargetsState().getDataPaths();
MavenModuleResourceConfiguration configuration = getModuleResourcesConfiguration(dataPaths);
HashStream64 hash = Hashing.komihash5_0().hashStream();
if (configuration != null) {
if (configuration == null) {
hash.putBoolean(false);
}
else {
hash.putBoolean(true);
configuration.computeConfigurationHash(isTests(), hash);
out.write(Long.toUnsignedString(hash.getAsLong(), Character.MAX_RADIX));
}
}
}

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.maven.model.impl;
import com.dynatrace.hash4j.hashing.HashStream64;
import com.dynatrace.hash4j.hashing.HashSink;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.ApiStatus;
@@ -21,7 +21,7 @@ public final class ResourceRootConfiguration extends FilePattern {
@Attribute("filtered")
public boolean isFiltered;
public void computeConfigurationHash(@NotNull HashStream64 hash) {
public void computeConfigurationHash(@NotNull HashSink hash) {
hash.putString(directory);
if (targetPath == null) {
hash.putInt(-1);