<commit_before>package org.bndtools.builder;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentHashMap;

import org.bndtools.api.BndtoolsConstants;
import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.bndtools.builder.classpath.BndContainerInitializer;
import org.bndtools.utils.swt.SWTConcurrencyUtil;
import org.bndtools.utils.workspace.FileUtils;
import org.bndtools.utils.workspace.WorkspaceUtils;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.Version;

import aQute.bnd.build.Project;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Descriptors.PackageRef;
import aQute.lib.collections.SortedList;
import aQute.lib.io.IO;
import bndtools.central.Central;
import bndtools.preferences.BndPreferences;
import bndtools.preferences.CompileErrorAction;

/**
 * This a Builder for bndtools.It will use the bnd project/workspace model to incrementally build bundles. This is a
 * rewrite of the NewBuilder. Left out are the Eclipse classpath include in a build option.
 *
 * <pre>
 *  create project test with 1 class
 *  Clean
 *  Add non-existent entry on -buildpath, should not build
 *  Remove non-existing entry, should remove errors and build
 *  delete output file, should rebuild
 *  create compile error -> no build
 *  remove compile error -> build again
 *  add Foo=foo to build.bnd -> check in test.jar
 *  add include bar.bnd to build.bnd, add Bar=bar -> check in test.jar
 *  touch bar.bnd -> see if manifest is updated in JAR (Jar viewer does not refresh very well, so reopen)
 *  touch build.bnd -> verify rebuild
 *  touch bnd.bnd in test -> verify rebuild
 * 
 *  create project test.2, add -buildpath: test
 * </pre>
 */
public class BndtoolsBuilder extends IncrementalProjectBuilder {
    public static final String PLUGIN_ID = "bndtools.builder";
    public static final String BUILDER_ID = BndtoolsConstants.BUILDER_ID;

    private static final ILogger logger = Logger.getLogger(BndtoolsBuilder.class);
    static final Set<Project> dirty = Collections.newSetFromMap(new ConcurrentHashMap<Project,Boolean>());
    static {
        CnfWatcher.install();
    }

    private Project model;
    private BuildLogger buildLog;
    private IProject[] dependsOn;
    private boolean postponed;

    /**
     * Called from Eclipse when it thinks this project should be build. We're proposed to figure out if we've changed
     * and then build as quickly as possible.
     * <p>
     * We ensure we're called in proper order defined by bnd, if not we will make it be called in proper order.
     *
     * @param kind
     * @param args
     * @param monitor
     * @return List of projects we depend on
     */
    @Override
    protected IProject[] build(int kind, Map<String,String> args, IProgressMonitor monitor) throws CoreException {

        BndPreferences prefs = new BndPreferences();
        buildLog = new BuildLogger(prefs.getBuildLogging());

        final CompileErrorAction actionOnCompileError = getActionOnCompileError();

        final BuildListeners listeners = new BuildListeners();
        int files = -1;

        final IProject myProject = getProject();
        try {
            MarkerSupport markers = new MarkerSupport(myProject);

            boolean force = kind == FULL_BUILD || kind == CLEAN_BUILD;

            //
            // First time after a restart
            //

            if (dependsOn == null) {
                dependsOn = myProject.getDescription().getDynamicReferences();
            }

            if (model == null) {
                try {
                    model = Central.getProject(myProject.getLocation().toFile());
                } catch (Exception e) {
                    markers.deleteMarkers("*");
                    markers.createMarker(null, IMarker.SEVERITY_ERROR, e.getMessage(), BndtoolsConstants.MARKER_BND_PROBLEM);
                }
                if (model == null)
                    return noreport();
            }

            model.clear();

            listeners.fireBuildStarting(myProject);

            DeltaWrapper delta = new DeltaWrapper(model, getDelta(myProject), buildLog);

            boolean setupChanged = false;

            if (!postponed && delta.havePropertiesChanged(model)) {
                buildLog.basic("project was dirty from changed bnd files postponed = " + postponed);
                model.forceRefresh();
                setupChanged = true;
            }

            if (dirty.remove(model)) {
                buildLog.basic("project was dirty from a workspace refresh postponed = " + postponed);
                setupChanged = true && !postponed;
            }

            //
            // If we already know we are going to build, we
            // must handle the path errors. We make sure
            // prepare() is called so we get any build errors.
            //

            if (force || setupChanged) {
                model.setChanged();
                model.setDelayRunDependencies(true);
                model.prepare();

                markers.setMarkers(model, BndtoolsConstants.MARKER_BND_PATH_PROBLEM);
                model.clear();

                dependsOn = calculateDependsOn(model);

                if (setBuildOrder(monitor)) {
                    buildLog.basic("Build order changed, postponing");
                    return postpone();
                }

                if (checkClasspathContainerUpdate(myProject)) {
                    // likely causes a recompile
                    buildLog.basic("classpaths were changed, postponing");
                    return postpone();<commit_msg>[builder] Did not check class path when dependencies changed The problem was that when the dependsOn had changed, we never checked the class path anymore, we postponed. Now it will always check the dependencies AND the class path. If any of the two indicates change, we postpone.<commit_after>package org.bndtools.builder;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentHashMap;

import org.bndtools.api.BndtoolsConstants;
import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.bndtools.builder.classpath.BndContainerInitializer;
import org.bndtools.utils.swt.SWTConcurrencyUtil;
import org.bndtools.utils.workspace.FileUtils;
import org.bndtools.utils.workspace.WorkspaceUtils;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.Version;

import aQute.bnd.build.Project;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Descriptors.PackageRef;
import aQute.lib.collections.SortedList;
import aQute.lib.io.IO;
import bndtools.central.Central;
import bndtools.preferences.BndPreferences;
import bndtools.preferences.CompileErrorAction;

/**
 * This a Builder for bndtools.It will use the bnd project/workspace model to incrementally build bundles. This is a
 * rewrite of the NewBuilder. Left out are the Eclipse classpath include in a build option.
 *
 * <pre>
 *  create project test with 1 class
 *  Clean
 *  Add non-existent entry on -buildpath, should not build
 *  Remove non-existing entry, should remove errors and build
 *  delete output file, should rebuild
 *  create compile error -> no build
 *  remove compile error -> build again
 *  add Foo=foo to build.bnd -> check in test.jar
 *  add include bar.bnd to build.bnd, add Bar=bar -> check in test.jar
 *  touch bar.bnd -> see if manifest is updated in JAR (Jar viewer does not refresh very well, so reopen)
 *  touch build.bnd -> verify rebuild
 *  touch bnd.bnd in test -> verify rebuild
 *
 *  create project test.2, add -buildpath: test
 * </pre>
 */
public class BndtoolsBuilder extends IncrementalProjectBuilder {
    public static final String PLUGIN_ID = "bndtools.builder";
    public static final String BUILDER_ID = BndtoolsConstants.BUILDER_ID;

    private static final ILogger logger = Logger.getLogger(BndtoolsBuilder.class);
    static final Set<Project> dirty = Collections.newSetFromMap(new ConcurrentHashMap<Project,Boolean>());
    static {
        CnfWatcher.install();
    }

    private Project model;
    private BuildLogger buildLog;
    private IProject[] dependsOn;
    private boolean postponed;

    /**
     * Called from Eclipse when it thinks this project should be build. We're proposed to figure out if we've changed
     * and then build as quickly as possible.
     * <p>
     * We ensure we're called in proper order defined by bnd, if not we will make it be called in proper order.
     *
     * @param kind
     * @param args
     * @param monitor
     * @return List of projects we depend on
     */
    @Override
    protected IProject[] build(int kind, Map<String,String> args, IProgressMonitor monitor) throws CoreException {

        BndPreferences prefs = new BndPreferences();
        buildLog = new BuildLogger(prefs.getBuildLogging());

        final CompileErrorAction actionOnCompileError = getActionOnCompileError();

        final BuildListeners listeners = new BuildListeners();
        int files = -1;

        final IProject myProject = getProject();
        try {
            MarkerSupport markers = new MarkerSupport(myProject);

            boolean force = kind == FULL_BUILD || kind == CLEAN_BUILD;

            //
            // First time after a restart
            //

            if (dependsOn == null) {
                dependsOn = myProject.getDescription().getDynamicReferences();
            }

            if (model == null) {
                try {
                    model = Central.getProject(myProject.getLocation().toFile());
                } catch (Exception e) {
                    markers.deleteMarkers("*");
                    markers.createMarker(null, IMarker.SEVERITY_ERROR, e.getMessage(), BndtoolsConstants.MARKER_BND_PROBLEM);
                }
                if (model == null)
                    return noreport();
            }

            model.clear();

            listeners.fireBuildStarting(myProject);

            DeltaWrapper delta = new DeltaWrapper(model, getDelta(myProject), buildLog);

            boolean setupChanged = false;

            if (!postponed && delta.havePropertiesChanged(model)) {
                buildLog.basic("project was dirty from changed bnd files postponed = " + postponed);
                model.forceRefresh();
                setupChanged = true;
            }

            if (dirty.remove(model)) {
                buildLog.basic("project was dirty from a workspace refresh postponed = " + postponed);
                setupChanged = true && !postponed;
            }

            //
            // If we already know we are going to build, we
            // must handle the path errors. We make sure
            // prepare() is called so we get any build errors.
            //

            if (force || setupChanged) {
                model.setChanged();
                model.setDelayRunDependencies(true);
                model.prepare();

                markers.setMarkers(model, BndtoolsConstants.MARKER_BND_PATH_PROBLEM);
                model.clear();

                dependsOn = calculateDependsOn(model);

                //
                // We have a setup change so we MUST check both class path
                // changes and build order changes. Careful not to use an OR
                // operation (as I did) because they are shortcutted. Since it
                // is also nice to see why we had a change, we just collect the
                // reason of the change so we can report it to the log.
                //

                String changed = ""; // if empty, no change
                String del = "";
                if (checkClasspathContainerUpdate(myProject)) {
                    changed += "Classpath container updated";
                    del = " & ";
                }

                if (setBuildOrder(monitor)) {
                    changed += del + "Build order changed";
                }

                if (!changed.equals("")) {
                    buildLog.basic("Setup changed: " + changed);
                    return postpone();
{'fraction_non_alphanumeric': 0.08872201362814629, 'fraction_numerical': 0.00041718815185648727, 'mean_word_length': 2.592157842157842, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3631954', 'n_tokens_mistral': 3977, 'n_tokens_neox': 3798, 'n_words': 1113}