All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.com.integradev.delphi.msbuild.DelphiProjectHelper Maven / Gradle / Ivy

The newest version!
/*
 * Sonar Delphi Plugin
 * Copyright (C) 2011 Sabre Airline Solutions and Fabricio Colombo
 * Author(s):
 * Przemyslaw Kociolek ([email protected])
 * Michal Wojcik ([email protected])
 * Fabricio Colombo ([email protected])
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package au.com.integradev.delphi.msbuild;

import static au.com.integradev.delphi.utils.DelphiUtils.inputFilesToPaths;
import static org.apache.commons.lang3.ArrayUtils.nullToEmpty;

import au.com.integradev.delphi.DelphiProperties;
import au.com.integradev.delphi.compiler.CompilerVersion;
import au.com.integradev.delphi.compiler.PredefinedConditionals;
import au.com.integradev.delphi.compiler.Toolchain;
import au.com.integradev.delphi.core.Delphi;
import au.com.integradev.delphi.enviroment.EnvironmentVariableProvider;
import au.com.integradev.delphi.utils.DelphiUtils;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.config.Configuration;
import org.sonar.api.scanner.ScannerSide;
import org.sonarsource.api.sonarlint.SonarLintSide;

@ScannerSide
@SonarLintSide
public class DelphiProjectHelper {
  private static final Logger LOG = LoggerFactory.getLogger(DelphiProjectHelper.class);
  private final Configuration settings;
  private final FileSystem fs;
  private final EnvironmentVariableProvider environmentVariableProvider;
  private final List projects;
  private final Toolchain toolchain;
  private final CompilerVersion compilerVersion;
  private final List searchDirectories;
  private final List debugSourceDirectories;
  private final List referencedFiles;
  private final Set conditionalDefines;
  private final Set unitScopeNames;
  private final Map unitAliases;
  private boolean indexedProjects;

  /**
   * Constructor
   *
   * @param settings Project settings
   * @param fs Sonar FileSystem
   */
  public DelphiProjectHelper(
      Configuration settings,
      FileSystem fs,
      EnvironmentVariableProvider environmentVariableProvider) {
    this.settings = settings;
    this.fs = fs;
    this.environmentVariableProvider = environmentVariableProvider;
    this.projects = new ArrayList<>();
    this.toolchain = getToolchainFromSettings();
    this.compilerVersion = getCompilerVersionFromSettings();
    this.searchDirectories = getSearchDirectoriesFromSettings();
    this.debugSourceDirectories = new ArrayList<>();
    this.referencedFiles = new ArrayList<>();
    this.conditionalDefines = getPredefinedConditionalDefines();
    this.unitScopeNames = getSetFromSettings(DelphiProperties.UNIT_SCOPE_NAMES_KEY);
    this.unitAliases = getUnitAliasesFromSettings();
  }

  private Set getSetFromSettings(String key) {
    return Arrays.stream(nullToEmpty(settings.getStringArray(key))).collect(Collectors.toSet());
  }

  private Toolchain getToolchainFromSettings() {
    return EnumUtils.getEnumIgnoreCase(
        Toolchain.class,
        settings.get(DelphiProperties.COMPILER_TOOLCHAIN_KEY).orElse(null),
        DelphiProperties.COMPILER_TOOLCHAIN_DEFAULT);
  }

  private CompilerVersion getCompilerVersionFromSettings() {
    String versionSymbol =
        settings
            .get(DelphiProperties.COMPILER_VERSION_KEY)
            .orElse(DelphiProperties.COMPILER_VERSION_DEFAULT.symbol());

    try {
      return CompilerVersion.fromVersionSymbol(versionSymbol);
    } catch (CompilerVersion.FormatException e) {
      LOG.warn(
          "Defaulting to compiler version \"{}\" because the provided one was invalid: \"{}\"",
          DelphiProperties.COMPILER_VERSION_DEFAULT,
          versionSymbol);
      LOG.debug("Exception: ", e);
      return DelphiProperties.COMPILER_VERSION_DEFAULT;
    }
  }

  private List getSearchDirectoriesFromSettings() {
    String[] searchDirectoriesSetting =
        nullToEmpty(settings.getStringArray(DelphiProperties.SEARCH_PATH_KEY));
    List result = new ArrayList<>();

    for (String path : searchDirectoriesSetting) {
      if (StringUtils.isBlank(path)) {
        continue;
      }
      File included = DelphiUtils.resolveAbsolutePath(fs.baseDir().getAbsolutePath(), path.trim());

      if (!included.exists()) {
        LOG.warn("{} {}", "Search path directory does not exist: ", included.getAbsolutePath());
      } else if (!included.isDirectory()) {
        LOG.warn("{} {}", "Search path item is not a directory: ", included.getAbsolutePath());
      } else {
        result.add(included.toPath());
      }
    }
    return result;
  }

  private Map getUnitAliasesFromSettings() {
    String[] aliases = nullToEmpty(settings.getStringArray(DelphiProperties.UNIT_ALIASES_KEY));
    Map result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
    Arrays.stream(aliases)
        .forEach(
            item -> {
              if (StringUtils.countMatches(item, '=') != 1) {
                LOG.warn("Invalid unit alias syntax: '{}'", item);
                return;
              }
              int equalIndex = item.indexOf('=');
              String unitAlias = item.substring(0, equalIndex).trim();
              String unitName = item.substring(equalIndex + 1).trim();
              result.put(unitAlias, unitName);
            });

    return result;
  }

  private Set getPredefinedConditionalDefines() {
    return PredefinedConditionals.getConditionalDefines(toolchain, compilerVersion);
  }

  private void indexProjects() {
    if (indexedProjects) {
      return;
    }

    FilePredicates p = fs.predicates();
    Iterable dprojFiles = fs.inputFiles(p.and(p.hasExtension("dproj")));
    Iterable gprojFiles = fs.inputFiles(p.and(p.hasExtension("groupproj")));

    if (dprojFiles == null) {
      dprojFiles = Collections.emptyList();
    }

    if (gprojFiles == null) {
      gprojFiles = Collections.emptyList();
    }

    LOG.info(
        "Indexing {} dproj file(s) and {} groupproj file(s)...",
        Iterables.size(dprojFiles),
        Iterables.size(gprojFiles));

    inputFilesToPaths(dprojFiles).forEach(this::indexProject);
    inputFilesToPaths(gprojFiles).forEach(this::indexProjectGroup);

    for (DelphiProject project : projects) {
      searchDirectories.addAll(project.getSearchDirectories());
      debugSourceDirectories.addAll(project.getDebugSourceDirectories());
      conditionalDefines.addAll(project.getConditionalDefines());
      referencedFiles.addAll(project.getSourceFiles());
      unitScopeNames.addAll(project.getUnitScopeNames());
      unitAliases.putAll(project.getUnitAliases());
    }

    conditionalDefines.addAll(getSetFromSettings(DelphiProperties.CONDITIONAL_DEFINES_KEY));
    conditionalDefines.removeAll(getSetFromSettings(DelphiProperties.CONDITIONAL_UNDEFINES_KEY));

    indexedProjects = true;
  }

  @VisibleForTesting
  Path environmentProjPath() {
    Path bdsPath = installationPath();
    if (bdsPath.getNameCount() < 3) {
      return null;
    }

    String appdata = environmentVariableProvider.getenv("APPDATA");
    if (appdata == null) {
      return null;
    }

    Path appdataPath = Path.of(appdata);
    String companyName = bdsPath.getParent().getParent().getFileName().toString();
    String productVersion = bdsPath.getFileName().toString();

    return appdataPath
        .resolve(companyName)
        .resolve("BDS")
        .resolve(productVersion)
        .resolve("environment.proj");
  }

  private void indexProject(Path dprojFile) {
    DelphiProjectParser parser =
        new DelphiProjectParser(dprojFile, environmentVariableProvider, environmentProjPath());
    DelphiProject newProject = parser.parse();
    projects.add(newProject);
  }

  private void indexProjectGroup(Path projectGroup) {
    DelphiProjectGroupParser parser =
        new DelphiProjectGroupParser(
            projectGroup, environmentVariableProvider, environmentProjPath());
    projects.addAll(parser.parse());
  }

  /**
   * Returns a path to the Delphi installation folder, as specified in settings
   *
   * @return Path to the installation folder
   */
  public Path installationPath() {
    String path =
        settings
            .get(DelphiProperties.INSTALLATION_PATH_KEY)
            .orElseThrow(
                () ->
                    new RuntimeException(
                        "Property '"
                            + DelphiProperties.INSTALLATION_PATH_KEY
                            + "' must be supplied."));

    return Path.of(path);
  }

  /**
   * Returns a path to the Delphi standard library, based on the installation path specified in
   * settings.
   *
   * @return Path to standard library
   */
  public Path standardLibraryPath() {
    return installationPath().resolve("source");
  }

  /**
   * Get the compiler version
   *
   * @return the compiler version
   */
  public CompilerVersion getCompilerVersion() {
    return compilerVersion;
  }

  /**
   * Get the compiler toolchain
   *
   * @return the compiler toolchain
   */
  public Toolchain getToolchain() {
    return toolchain;
  }

  /**
   * Gets the search directories specified in settings and project files
   *
   * @return List of search path directories
   */
  public List getSearchDirectories() {
    indexProjects();
    return searchDirectories;
  }

  /**
   * Gets the debug source directories specified in project files
   *
   * @return List of debug source directorie
   */
  public List getDebugSourceDirectories() {
    indexProjects();
    return debugSourceDirectories;
  }

  /**
   * Gets the set of conditional defines specified in settings and project files
   *
   * @return set of conditional defines
   */
  public Set getConditionalDefines() {
    indexProjects();
    return conditionalDefines;
  }

  /**
   * Gets the set of unit scope names specified in settings and project files
   *
   * @return set of unit scope names
   */
  public Set getUnitScopeNames() {
    indexProjects();
    return unitScopeNames;
  }

  /**
   * Gets the map of unit aliases specified in settings and project files
   *
   * @return map of unit aliases
   */
  public Map getUnitAliases() {
    indexProjects();
    return unitAliases;
  }

  public List getReferencedFiles() {
    indexProjects();
    return referencedFiles;
  }

  public Iterable inputFiles() {
    FilePredicates p = fs.predicates();
    return fs.inputFiles(p.and(p.hasLanguage(Delphi.KEY)));
  }

  public boolean shouldExecuteOnProject() {
    return fs.hasFiles(fs.predicates().hasLanguage(Delphi.KEY));
  }

  public InputFile getFile(String path) {
    return fs.inputFile(fs.predicates().hasURI(Paths.get(path).toUri()));
  }

  public InputFile getFileFromBasename(String basename) {
    return fs.inputFile(fs.predicates().hasFilename(basename));
  }

  public String encoding() {
    return fs != null ? fs.encoding().name() : Charset.defaultCharset().name();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy