blob: cd94da9f5653d795258b842f126549003525275b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020 RBEI and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v. 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Adhith Gopal - Initial contribution and API
*******************************************************************************/
package org.eclipse.blockchain.core;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.eclipse.blockchain.core.events.BlockchainBuildEvent;
import org.eclipse.blockchain.core.events.BlockchainBuildTrigger;
import org.eclipse.blockchain.core.events.IBlockchainBuildEvent;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
/**
* Ethereum Project builder
*/
public class EthereumBuilder extends IncrementalProjectBuilder {
/**
* Ethereum Project builder
*/
public static final String ETHEREUM_BUILDER = "org.eclipse.blockchain.core.ethbuilder";
private static final String SOLIDITY_OUTPUT_FOLDER = "sol-output";
private static ExecutorService service = Executors.newFixedThreadPool(4);
private final List<String> compileErrors = new ArrayList<>();
private boolean change = false;
/**
* {@inheritDoc}
*/
@Override
protected IProject[] build(final int kind, final Map<String, String> args, final IProgressMonitor monitor)
throws CoreException {
this.compileErrors.clear();
this.change = false;
try {
if ((IncrementalProjectBuilder.INCREMENTAL_BUILD == kind)
|| (IncrementalProjectBuilder.AUTO_BUILD == kind)) {
IResourceDelta changes = getDelta(getProject());
if (changes != null) {
incrementalBuild(changes);
} else {
clean(monitor);
fullBuild();
}
} else if (IncrementalProjectBuilder.FULL_BUILD == kind) {
clean(monitor);
fullBuild();
}
if (this.change) {
triggerBuildFinished();
}
} catch (ExecutionException | InterruptedException e) {
BlockchainCore.getInstance().logException("", e.getMessage(), e);
}
return new IProject[0];
}
private void triggerBuildFinished() {
IBlockchainBuildEvent event = new BlockchainBuildEvent(this.compileErrors, getProject());
new Thread(() -> {
BlockchainBuildTrigger.getInstance().notifyListeners(event);
}, "Blockchain Build Finished").start();
}
private void fullBuild() throws CoreException, InterruptedException, ExecutionException {
Set<IResource> changedFiles = new HashSet<>();
List<Future<String>> scheduledFutures = new ArrayList<>();
getProject().accept((resource) -> {
if (resource != null) {
String fileExtension = resource.getFileExtension();
if ((fileExtension != null) && fileExtension.equals("sol")) {
changedFiles.add(resource);
}
}
return true;
});
for (IResource resource : changedFiles) {
this.change = true;
scheduledFutures.add(scheduleCompile(resource));
}
waitForFuturesToComplete(scheduledFutures);
}
private void incrementalBuild(final IResourceDelta changes)
throws CoreException, InterruptedException, ExecutionException {
Set<IResource> changedFiles = new HashSet<>();
List<Future<String>> scheduledFutures = new ArrayList<>();
changes.accept((final IResourceDelta delta) -> {
IResource resource = delta.getResource();
if (resource != null) {
String fileExtension = resource.getFileExtension();
if ((fileExtension != null) && fileExtension.equals("sol")) {
changedFiles.add(resource);
}
}
return true;
});
for (IResource resource : changedFiles) {
this.change = true;
scheduledFutures.add(scheduleCompile(resource));
}
waitForFuturesToComplete(scheduledFutures);
}
private void waitForFuturesToComplete(final List<Future<String>> scheduledFutures)
throws InterruptedException, ExecutionException {
while (!scheduledFutures.isEmpty()) {
List<Future<String>> futuresToRemove = new ArrayList<>();
for (Future<String> scheduledFuture : scheduledFutures) {
if (scheduledFuture.isDone() || scheduledFuture.isCancelled()) {
String result = scheduledFuture.get();
if (!result.isEmpty()) {
this.compileErrors.add(result);
}
futuresToRemove.add(scheduledFuture);
}
}
if (!futuresToRemove.isEmpty()) {
scheduledFutures.removeAll(futuresToRemove);
}
Thread.sleep(100);
}
}
private Future<String> scheduleCompile(final IResource resourceToCompile) {
List<IResource> resourceToCompileAsSet = new ArrayList<>();
resourceToCompileAsSet.add(resourceToCompile);
return service.submit(() -> {// NOSONAR
return CoreCommandExecutor.getInstance().solidityCompile(getProject().getName(), resourceToCompileAsSet,
getProject().getFolder(SOLIDITY_OUTPUT_FOLDER));
});
}
/**
* This cleans the solidity output folder {@inheritDoc}
*/
@Override
protected void clean(final IProgressMonitor monitor) throws CoreException {
IProject project = getProject();
IFolder solidityOutputFolder = project.getFolder(SOLIDITY_OUTPUT_FOLDER);
if (solidityOutputFolder.exists()) {
solidityOutputFolder.delete(true, new NullProgressMonitor());
}
}
}