Beispiel #1
0
    def __init__(self, log: Log, variableProcessor: VariableProcessor,
                 recipeBuilderSetup: Optional[RecipeBuilderSetup],
                 platformName: str, cmakeConfig: GeneratorCMakeConfig) -> None:
        super().__init__()

        self.__Log = log  # type: Log
        self.__VariableProcessor = variableProcessor  # type: VariableProcessor

        self.IsEnabled = recipeBuilderSetup is not None  # type: bool

        self.TargetLocation = None  # Optional[ResolvedPath]
        self.DownloadCacheRootPath = None  # Optional[str]
        self.__TempRootPath = None  # Optional[str]
        self.__TempPipelineRootPath = None  # Optional[str]
        self.InstallRootLocation = None  # Optional[ResolvedPath]
        self.ReadonlyCache_DownloadCacheRootPath = None  # Optional[str]

        if self.IsEnabled and recipeBuilderSetup is not None:
            targetLocation = recipeBuilderSetup.TargetLocation
            readonlyCachePath = recipeBuilderSetup.ReadonlyCachePath

            if not IOUtil.IsAbsolutePath(targetLocation.ResolvedPath):
                raise Exception(
                    "Install area path is not absolute: '{0}'".format(
                        targetLocation.ResolvedPath))
            if not readonlyCachePath is None and not IOUtil.IsAbsolutePath(
                    readonlyCachePath):
                raise Exception(
                    "Install area readonly cache path is not absolute: '{0}'".
                    format(readonlyCachePath))

            self.TargetLocation = targetLocation
            self.DownloadCacheRootPath = IOUtil.Join(
                targetLocation.ResolvedPath, ".DownloadCache")

            self.__TempRootPath = IOUtil.Join(targetLocation.ResolvedPath,
                                              ".Temp")

            baseTempDirectory = IOUtil.Join(self.__TempRootPath, "pipeline")
            baseTempDirectory = IOUtil.Join(baseTempDirectory, platformName)
            self.__TempPipelineRootPath = IOUtil.Join(
                baseTempDirectory, cmakeConfig.GeneratorRecipeShortName)

            sourceBaseInstallDirectory = IOUtil.Join(targetLocation.SourcePath,
                                                     platformName)
            sourceInstallRootPath = IOUtil.Join(
                sourceBaseInstallDirectory,
                cmakeConfig.GeneratorRecipeShortName)

            baseInstallDirectory = IOUtil.Join(targetLocation.ResolvedPath,
                                               platformName)
            installRootPath = IOUtil.Join(baseInstallDirectory,
                                          cmakeConfig.GeneratorRecipeShortName)

            self.InstallRootLocation = ResolvedPath(sourceInstallRootPath,
                                                    installRootPath)

            self.ReadonlyCache_DownloadCacheRootPath = None if readonlyCachePath is None else IOUtil.Join(
                readonlyCachePath, ".DownloadCache")
    def __init__(self, log: Log, variableProcessor: VariableProcessor,
                 recipeBuilderSetup: Optional[RecipeBuilderSetup],
                 platformName: str, compilerGeneratorName: str) -> None:
        super(RecipePathBuilder, self).__init__()

        self.__Log = log  # type: Log
        self.__VariableProcessor = variableProcessor  # type: VariableProcessor

        self.IsEnabled = recipeBuilderSetup is not None  # type: bool

        self.TargetPath = None  # Optional[str]
        self.DownloadCacheRootPath = None  # Optional[str]
        self.__TempRootPath = None  # Optional[str]
        self.__TempPipelineRootPath = None  # Optional[str]
        self.InstallRootPath = None  # Optional[str]
        self.ReadonlyCache_DownloadCacheRootPath = None  # Optional[str]

        if self.IsEnabled and recipeBuilderSetup is not None:
            targetPath = recipeBuilderSetup.TargetPath
            readonlyCachePath = recipeBuilderSetup.ReadonlyCachePath

            if not IOUtil.IsAbsolutePath(targetPath):
                raise Exception(
                    "Install area path is not absolute: '{0}'".format(
                        targetPath))
            if not readonlyCachePath is None and not IOUtil.IsAbsolutePath(
                    readonlyCachePath):
                raise Exception(
                    "Install area readonly cache path is not absolute: '{0}'".
                    format(readonlyCachePath))

            self.TargetPath = targetPath
            self.DownloadCacheRootPath = IOUtil.Join(targetPath,
                                                     ".DownloadCache")

            self.__TempRootPath = IOUtil.Join(targetPath, ".Temp")

            baseTempDirectory = IOUtil.Join(self.__TempRootPath, "pipeline")
            baseTempDirectory = IOUtil.Join(baseTempDirectory, platformName)
            self.__TempPipelineRootPath = IOUtil.Join(baseTempDirectory,
                                                      compilerGeneratorName)

            baseInstallDirectory = IOUtil.Join(targetPath, platformName)
            self.InstallRootPath = IOUtil.Join(baseInstallDirectory,
                                               compilerGeneratorName)

            self.ReadonlyCache_DownloadCacheRootPath = None if readonlyCachePath is None else IOUtil.Join(
                readonlyCachePath, ".DownloadCache")
Beispiel #3
0
    def __GetEnvironmentVariable(self, name: str) -> str:
        # For cache entries we allow the variable to not be defined, but if it is defned we retrieve is as normal
        value = IOUtil.TryGetEnvironmentVariable(name)
        if value is None:
            raise EnvironmentError(
                "{0} environment variable not set".format(name))

        value = IOUtil.NormalizePath(value)
        if value is None:
            raise EnvironmentError(
                "{0} environment variable not set".format(name))

        if not IOUtil.IsAbsolutePath(value):
            raise EnvironmentError(
                "{0} environment path '{1}' is not absolute".format(
                    name, value))

        if value.endswith("/"):
            raise EnvironmentError(
                "{0} environment path '{1}' not allowed to end with '/' or '\'"
                .format(name, value))

        # Create the directory if it didnt exist
        if not IOUtil.IsDirectory(value) and not IOUtil.Exists(value):
            self.__Log.LogPrint(
                "The directory '{0}' did not exist, creating it".format(value))
            IOUtil.SafeMakeDirs(value)

        if not IOUtil.IsDirectory(value):
            raise EnvironmentError(
                "The {0} environment variable content '{1}' does not point to a valid directory"
                .format(name, value))
        return value
Beispiel #4
0
    def __init__(self, filename: str, strPackageName: Optional[str],
                 packageLocation: ToolConfigPackageLocation) -> None:
        super(PackageFile, self).__init__()

        if not IOUtil.IsAbsolutePath(filename):
            raise UsageErrorException()

        if not isinstance(packageLocation, ToolConfigPackageLocation):
            raise UsageErrorException()

        filename = IOUtil.NormalizePath(filename)
        if not filename.startswith(packageLocation.ResolvedPathEx):
            raise UsageErrorException(
                "The filename '{0}' does not belong to the supplied location '{1}'"
                .format(filename, packageLocation.ResolvedPathEx))

        self.Filename = IOUtil.GetFileName(filename)
        self.RelativeFilePath = filename[
            len(packageLocation.ResolvedPathEx
                ):]  # The full relative path to the file
        self.RelativeDirPath = IOUtil.GetDirectoryName(
            self.RelativeFilePath)  # The relative containing directory
        self.AbsoluteFilePath = filename  # type: str
        self.AbsoluteDirPath = IOUtil.GetDirectoryName(filename)  # type: str
        self.PackageRootLocation = packageLocation  # type: ToolConfigPackageLocation
        self.PackageName = self.__DeterminePackageNameFromRelativeName(
            self.RelativeDirPath
        ) if strPackageName is None else strPackageName  # type: str
Beispiel #5
0
 def __init__(self, log: Log, pythonScriptRoot: str) -> None:
     super().__init__()
     if not IOUtil.IsAbsolutePath(pythonScriptRoot):
         raise Exception("pythonScriptRoot '{0}' is not absolute".format(
             pythonScriptRoot))
     self.Log = log
     self.PythonScriptRoot = pythonScriptRoot  # type: str
     self.EnvDict = {}  # type: Dict[str,str]
    def __init__(self, packagePath: str, packageRelativeFilePath: str) -> None:
        packagePath = IOUtil.NormalizePath(packagePath)
        packageRelativeFilePath = IOUtil.NormalizePath(packageRelativeFilePath)

        if not IOUtil.IsAbsolutePath(packagePath):
            raise Exception("packagePath must be absolute")
        if IOUtil.IsAbsolutePath(packageRelativeFilePath):
            raise Exception("packageRelativeFilePath can not be absolute")

        self.Filename = IOUtil.GetFileName(packageRelativeFilePath)

        absFilePath = IOUtil.Join(packagePath, packageRelativeFilePath)
        self.AbsoluteFilePath = absFilePath
        self.AbsoluteDirPath = IOUtil.GetDirectoryName(absFilePath)

        self.PackageRelativeFilePath = packageRelativeFilePath
        self.PackageRelativeDirPath = IOUtil.GetDirectoryName(
            packageRelativeFilePath)
Beispiel #7
0
 def __init__(self, userRequestedFNMatchPattern: str) -> None:
     super().__init__()
     userRequestedFNMatchPattern = IOUtil.NormalizePath(
         userRequestedFNMatchPattern)
     #self.FilterDirPath = IOUtil.NormalizePath(filterDirPath)
     self.UserRequestedFNMatchPattern = userRequestedFNMatchPattern
     self.IsAbsolutePattern = IOUtil.IsAbsolutePath(
         userRequestedFNMatchPattern)
     self.PatternFileName = IOUtil.GetFileName(userRequestedFNMatchPattern)
     patternDirectory = IOUtil.GetDirectoryName(userRequestedFNMatchPattern)
     if not self.IsAbsolutePattern and len(
             patternDirectory) > 0 and not patternDirectory.startswith('/'):
         patternDirectory = '/' + patternDirectory
     self.PatternDirectory = patternDirectory
    def __init__(self, filename: str, strPackageName: Optional[str],
                 packageLocation: ToolConfigPackageLocation) -> None:
        filename = IOUtil.NormalizePath(filename)
        if not IOUtil.IsAbsolutePath(filename):
            raise UsageErrorException()

        rootRelativePath = filename[len(packageLocation.ResolvedPathEx):]
        super().__init__(IOUtil.GetDirectoryName(rootRelativePath),
                         packageLocation, False)

        self.Filename = IOUtil.GetFileName(filename)
        self.RootRelativeFilePath = rootRelativePath  # The full root relative path to the file
        self.AbsoluteFilePath = filename  # type: str
        self.PackageName = self.__DeterminePackageNameFromRelativeName(
            self.RootRelativeDirPath
        ) if strPackageName is None else strPackageName  # type: str
Beispiel #9
0
 def __init__(self, log: Log, xmlElement: ET.Element) -> None:
     super().__init__(log, xmlElement)
     self.Name = self._ReadAttrib(xmlElement, 'Name')
     self.Version = self._TryReadAttribAsVersion(
         xmlElement, 'Version')  # type: Optional[Version]
     self.TargetName = self._TryReadAttrib(
         xmlElement, 'TargetName')  # type: Optional[str]
     # A optional path that will be specified to cmake
     self.Path = self._TryReadAttrib(xmlElement,
                                     'Path')  # type: Optional[str]
     self.IfCondition = self._TryReadAttrib(
         xmlElement, 'If',
         DependencyCondition.FindPackageAllowed)  # type: Optional[str]
     if self.IfCondition != DependencyCondition.FindPackageAllowed:
         raise XmlFormatException(
             "Unsupported IfCondition '{0}' on FindPackage: '{1}'. Expected {2}"
             .format(self.Version, self.Name,
                     DependencyCondition.FindPackageAllowed))
     if self.Path is not None and IOUtil.IsAbsolutePath(self.Path):
         raise XmlFormatException("Path '{0}' can not be absolute".format(
             self.Path))
    def __init__(self, path: str, packageLocation: ToolConfigPackageLocation, normalize: bool = True) -> None:
        super().__init__()

        path = IOUtil.NormalizePath(path) if normalize else path

        if not isinstance(packageLocation, ToolConfigPackageLocation):
            raise UsageErrorException()


        if IOUtil.IsAbsolutePath(path):
            if not path.startswith(packageLocation.ResolvedPathEx):
                raise UsageErrorException("The path '{0}' does not belong to the supplied location '{1}'".format(path, packageLocation.ResolvedPathEx))
            rootRelativeDirPath = path[len(packageLocation.ResolvedPathEx):]
            absoluteDirPath = path
        else:
            rootRelativeDirPath = path
            absoluteDirPath = IOUtil.Join(packageLocation.ResolvedPath, path)

        self.RootRelativeDirPath = rootRelativeDirPath  # The root relative containing directory
        self.AbsoluteDirPath = absoluteDirPath          # type: str
        self.PackageRootLocation = packageLocation      # type: ToolConfigPackageLocation
    def Process(self, currentDirPath: str, toolConfig: ToolConfig,
                localToolConfig: LocalToolConfig) -> None:
        config = Config(self.Log, toolConfig,
                        localToolConfig.PackageConfigurationType,
                        localToolConfig.BuildVariantsDict,
                        localToolConfig.AllowDevelopmentPlugins)

        # create a config we control and that can be used to just build the tool recipe's
        configToolCheck = Config(self.Log, toolConfig,
                                 PluginSharedValues.TYPE_DEFAULT,
                                 localToolConfig.BuildVariantsDict,
                                 localToolConfig.AllowDevelopmentPlugins)
        if localToolConfig.DryRun:
            config.ForceDisableAllWrite()
            configToolCheck.ForceDisableAllWrite()

        self.__CheckUserArgs(localToolConfig.ClangFormatArgs, "formatArgs")
        self.__CheckUserArgs(localToolConfig.ClangTidyArgs, "tidyArgs")
        self.__CheckUserArgs(localToolConfig.ClangTidyPostfixArgs,
                             "tidyPostfixArgs")

        applyClangFormat = toolConfig.ClangFormatConfiguration is not None and localToolConfig.ClangFormat
        applyClangTidy = toolConfig.ClangTidyConfiguration is not None and localToolConfig.ClangTidy

        if localToolConfig.IgnoreNotSupported or (
            (localToolConfig.ScanSource or applyClangFormat)
                and not applyClangTidy):
            config.IgnoreNotSupported = True
            configToolCheck.IgnoreNotSupported = True

        packageFilters = localToolConfig.BuildPackageFilters

        # Get the platform and see if its supported
        platform = PluginConfig.GetGeneratorPluginById(
            localToolConfig.PlatformName, False)
        PlatformUtil.CheckBuildPlatform(platform.Name)
        generatorContext = GeneratorContext(config,
                                            config.ToolConfig.Experimental,
                                            platform)

        config.LogPrint("Active platform: {0}".format(platform.Name))

        packageRecipeResultManager = None  # type: Optional[PackageRecipeResultManager]
        toolPackageNames = []
        if applyClangFormat or applyClangTidy:
            if applyClangFormat:
                if toolConfig.ClangFormatConfiguration is None:
                    raise Exception("internal error")
                toolPackageNames.append(
                    toolConfig.ClangFormatConfiguration.RecipePackageName)
            if applyClangTidy:
                if toolConfig.ClangTidyConfiguration is None:
                    raise Exception("internal error")
                toolPackageNames.append(
                    toolConfig.ClangTidyConfiguration.RecipePackageName)
            packageRecipeResultManager = ForceCheckBuildTools(
                configToolCheck, generatorContext, toolPackageNames)

        searchDir = currentDirPath
        if localToolConfig.File is not None and IOUtil.IsAbsolutePath(
                localToolConfig.File):
            searchDir = IOUtil.GetDirectoryName(localToolConfig.File)
        closestGenFilePath = FileFinder.TryFindClosestFileInRoot(
            config, toolConfig, searchDir, config.GenFileName)
        if closestGenFilePath is None:
            closestGenFilePath = searchDir

        if (self.Log.Verbosity >= 4):
            self.Log.LogPrint("Closest '{0}' file path: '{1}'".format(
                toolConfig.GenFileName, closestGenFilePath))

        packageProcess = None  # type: Optional[MainFlow.PackageLoadAndResolveProcess]
        packages = None
        discoverFeatureList = '*' in packageFilters.FeatureNameList
        if discoverFeatureList or localToolConfig.Project is None or localToolConfig.ScanSource or applyClangFormat or applyClangTidy:
            if discoverFeatureList:
                config.LogPrint(
                    "No features specified, so using package to determine them"
                )
            if localToolConfig.ScanSource or applyClangFormat or applyClangTidy or discoverFeatureList:
                packageProcess = self.__CreatePackageProcess(
                    config, toolConfig.GetMinimalConfig(), closestGenFilePath,
                    localToolConfig.Recursive, generatorContext.Platform,
                    toolPackageNames)
                packageProcess.Resolve(generatorContext, packageFilters,
                                       applyClangTidy, False)
                packages = packageProcess.Packages
                topLevelPackage = PackageListUtil.GetTopLevelPackage(packages)
                if discoverFeatureList:
                    packageFilters.FeatureNameList = [
                        entry.Name
                        for entry in topLevelPackage.ResolvedAllUsedFeatures
                    ]

        customPackageFileFilter = None  # type: Optional[CustomPackageFileFilter]
        if not localToolConfig.ScanSource and not applyClangFormat and not applyClangTidy:
            Validate.ValidatePlatform(config, localToolConfig.PlatformName,
                                      packageFilters.FeatureNameList)
            if packageProcess is None:
                packageProcess = self.__CreatePackageProcess(
                    config, toolConfig.GetMinimalConfig(), closestGenFilePath,
                    localToolConfig.Recursive, generatorContext.Platform,
                    toolPackageNames)
            if not packageProcess.IsFullResolve or packages is None:
                # For now this requires a full resolve (but basically it only requires basic + files)
                packages = packageProcess.Resolve(generatorContext,
                                                  packageFilters,
                                                  applyClangTidy, True)

            topLevelPackage = PackageListUtil.GetTopLevelPackage(packages)
            RecipeBuilder.ValidateInstallationForPackages(
                config, generatorContext, topLevelPackage.ResolvedBuildOrder)
        else:
            if localToolConfig.File is not None:
                # Delay extension validation
                customPackageFileFilter = CustomPackageFileFilter(
                    localToolConfig.File)

        theTopLevelPackage = None  # type: Optional[Package]
        filteredPackageList = []  # type: List[Package]
        if applyClangTidy or applyClangFormat or localToolConfig.ScanSource:
            addExternals = applyClangTidy
            filteredPackageList, theTopLevelPackage = self.__PreparePackages(
                self.Log, localToolConfig, packageProcess, generatorContext,
                packageFilters, addExternals, packages, config.IsSDKBuild,
                applyClangTidy, config)
            if len(filteredPackageList) <= 0:
                self.Log.DoPrint("No supported packages left to process")
                return

        if applyClangTidy:
            self.__ApplyClangTidy(self.Log, toolConfig, localToolConfig,
                                  packageRecipeResultManager,
                                  theTopLevelPackage, filteredPackageList,
                                  platform, config, generatorContext,
                                  customPackageFileFilter)

        if applyClangFormat:
            self.__ApplyClangFormat(self.Log, toolConfig, localToolConfig,
                                    packageRecipeResultManager,
                                    filteredPackageList,
                                    customPackageFileFilter)

        # Scan source after 'format' to ensure we dont warn about stuff that has been fixed
        if localToolConfig.ScanSource:
            self.__ApplyScanSource(self.Log, localToolConfig,
                                   config.IsSDKBuild, config.DisableWrite,
                                   filteredPackageList,
                                   customPackageFileFilter)
Beispiel #12
0
    def Capture(log: Log, runCommand: List[str], pythonScriptRoot: str, envNameList: List[str]) -> Dict[str, str]:
        """
        param: runCommand: the command we should run.
        param: pythonScriptRoot: The root location of the FslBuild.py tools (this must be a absolute path)
                                 This will be joined with the FslBuildDump.py name to get the absolute path to the python script.
        """
        if not IOUtil.IsAbsolutePath(pythonScriptRoot):
            raise Exception("pythonScriptRoot '{0}' is not absolute".format(pythonScriptRoot))

        # Validate that the input is in the correct format and create a initial dict
        entryDict = {} # type: Dict[str,str]
        for envEntry in envNameList:
            if not (envEntry.startswith("$(") and envEntry.endswith(")")):
                raise Exception("Environment variable not in the correct $(NAME) format {0}".format(envEntry))
            strippedEntry = envEntry[2:-1]
            entryDict[envEntry] = strippedEntry

        log.LogPrintVerbose(4, "Trying to capture environment variables: {0}".format(envNameList))

        # call a custom python script with the stripped argument list using the 'generator' run script
        # the scripts main role is to dump the listed environment variables to screen in a 'json dictionary' format that can be easily captured and
        # decoded.

        # Call generator run script (captured)
        # 'use the "FslBuildDumpEnv --Env [ENV1,ENV2]" script to generate a json dump for the env variables
        strEnv = "[{0}]".format(",".join(entryDict.values()))
        commandName = 'FslBuildDumpEnv.py'
        absCommandName = IOUtil.Join(pythonScriptRoot, commandName)
        cmdList = [absCommandName, '--AllowNotFound', '--Enclose', '--Env', strEnv]
        runCommand += cmdList
        content = LocalUtil.TryRun(log, runCommand)
        if content is None:
            raise Exception("Failed to capture virtual variant environment variables {0}".format(envNameList))

        # Parse captured json output
        strJson = LocalUtil.ExtractJson(content)

        # Decode json
        jsonDict = json.loads(strJson)

        finalDict = {} # type: Dict[str,str]
        for key, value in jsonDict.items():
            if not isinstance(key, str) or not isinstance(value, str):
                LocalUtil.DumpCapture(log, 4, content)
                raise Exception("captured json decode failed")
            if not key in jsonDict:
                LocalUtil.DumpCapture(log, 4, content)
                raise Exception("Capture contained wrong key: '{0}".format(key))
            finalDict[key] = value

        # Ensure that all the requested entries are present
        if len(finalDict) != len(entryDict):
            missingKeys = []
            for key in entryDict:
                if not key in finalDict:
                    missingKeys.append(key)
            LocalUtil.DumpCapture(log, 4, content)
            raise Exception("The Captured environment variable output is missing for: {0}".format(missingKeys))

        log.LogPrintVerbose(4, "Captured environment variables: {0}".format(finalDict))

        return finalDict
Beispiel #13
0
    def Process(self, currentDirPath: str, toolConfig: ToolConfig,
                localToolConfig: LocalToolConfig) -> None:

        #self.Log.LogPrintVerbose(2, "*** Forcing the legacy clang tidy mode ***")
        #localToolConfig.Legacy = True

        config = Config(self.Log, toolConfig,
                        localToolConfig.PackageConfigurationType,
                        localToolConfig.BuildVariantsDict,
                        localToolConfig.AllowDevelopmentPlugins)

        # create a config we control and that can be used to just build the tool recipe's
        configToolCheck = Config(self.Log, toolConfig,
                                 PluginSharedValues.TYPE_DEFAULT,
                                 localToolConfig.BuildVariantsDict,
                                 localToolConfig.AllowDevelopmentPlugins)
        if localToolConfig.DryRun:
            config.ForceDisableAllWrite()
            configToolCheck.ForceDisableAllWrite()

        self.__CheckUserArgs(localToolConfig.ClangFormatArgs, "formatArgs")
        self.__CheckUserArgs(localToolConfig.ClangTidyArgs, "tidyArgs")
        self.__CheckUserArgs(localToolConfig.ClangTidyPostfixArgs,
                             "tidyPostfixArgs")

        applyClangFormat = toolConfig.ClangFormatConfiguration is not None and localToolConfig.ClangFormat
        applyClangTidy = toolConfig.ClangTidyConfiguration is not None and localToolConfig.ClangTidy

        if localToolConfig.IgnoreNotSupported or (
            (localToolConfig.ScanSource or applyClangFormat)
                and not applyClangTidy):
            config.IgnoreNotSupported = True
            configToolCheck.IgnoreNotSupported = True

        packageFilters = localToolConfig.BuildPackageFilters

        # Get the platform and see if its supported
        buildVariantConfig = BuildVariantConfigUtil.GetBuildVariantConfig(
            localToolConfig.BuildVariantsDict)
        if localToolConfig.Legacy and applyClangTidy and config.ToolConfig.CMakeConfiguration is not None:
            # For the LEGACY clangTidy implementation we disable allow find package for the build checks for now as we dont use cmake for those
            # We basically have to update the tidy pass to utilize ninja+cmake for the tidy pass so that find_package will work
            self.Log.LogPrintVerbose(2, "Force disabling 'AllowFindPackage'")
            config.ToolConfig.CMakeConfiguration.SetAllowFindPackage(False)

        cmakeUserConfig = localToolConfig.GetUserCMakeConfig()
        if not localToolConfig.Legacy and applyClangTidy:
            config.LogPrintVerbose(
                2, "Forcing the ninja generator for clang tidy")
            cmakeUserConfig.GeneratorName = "Ninja"

        generator = self.ToolAppContext.PluginConfigContext.GetGeneratorPluginById(
            localToolConfig.PlatformName, localToolConfig.Generator,
            buildVariantConfig, config.ToolConfig.DefaultPackageLanguage,
            config.ToolConfig.CMakeConfiguration, cmakeUserConfig, True)
        PlatformUtil.CheckBuildPlatform(generator.PlatformName)
        generatorContext = GeneratorContext(config, self.ErrorHelpManager,
                                            packageFilters.RecipeFilterManager,
                                            config.ToolConfig.Experimental,
                                            generator)

        self.Log.LogPrint("Active platform: {0}".format(
            generator.PlatformName))

        packageRecipeResultManager = None  # type: Optional[PackageRecipeResultManager]
        toolPackageNamesSet = set()
        toolPackageNames = []
        if applyClangFormat or applyClangTidy:
            if applyClangFormat:
                if toolConfig.ClangFormatConfiguration is None:
                    raise Exception("internal error")
                toolPackageNamesSet.add(
                    toolConfig.ClangFormatConfiguration.RecipePackageName)
                toolPackageNamesSet.add(
                    toolConfig.ClangFormatConfiguration.NinjaRecipePackageName)
            if applyClangTidy:
                if toolConfig.ClangTidyConfiguration is None:
                    raise Exception("internal error")
                toolPackageNamesSet.add(
                    toolConfig.ClangTidyConfiguration.ClangRecipePackageName)
                toolPackageNamesSet.add(toolConfig.ClangTidyConfiguration.
                                        ClangTidyRecipePackageName)
                toolPackageNamesSet.add(
                    toolConfig.ClangTidyConfiguration.NinjaRecipePackageName)
            toolPackageNames = list(toolPackageNamesSet)
            packageRecipeResultManager = ForceCheckBuildTools(
                configToolCheck, generatorContext, toolPackageNames)

        searchDir = currentDirPath
        if localToolConfig.File is not None:
            localToolConfig.File = IOUtil.NormalizePath(localToolConfig.File)
            if IOUtil.IsAbsolutePath(localToolConfig.File):
                searchDir = IOUtil.GetDirectoryName(localToolConfig.File)
        closestGenFilePath = FileFinder.TryFindClosestFileInRoot(
            config, toolConfig, searchDir, config.GenFileName)
        if closestGenFilePath is None:
            closestGenFilePath = searchDir

        if self.Log.Verbosity >= 4:
            self.Log.LogPrint("Closest '{0}' file path: '{1}'".format(
                toolConfig.GenFileName, closestGenFilePath))

        packageProcess = None  # type: Optional[MainFlow.PackageLoadAndResolveProcess]
        packages = None
        discoverFeatureList = '*' in packageFilters.FeatureNameList
        if discoverFeatureList or localToolConfig.Project is None or localToolConfig.ScanSource or applyClangFormat or applyClangTidy:
            if discoverFeatureList:
                config.LogPrint(
                    "No features specified, so using package to determine them"
                )
            if localToolConfig.ScanSource or applyClangFormat or applyClangTidy or discoverFeatureList:
                packageProcess = self.__CreatePackageProcess(
                    config, toolConfig.GetMinimalConfig(generator.CMakeConfig),
                    closestGenFilePath, localToolConfig.Recursive,
                    generatorContext.Platform, toolPackageNames)
                packageProcess.Resolve(generatorContext, packageFilters,
                                       applyClangTidy, False)
                packages = packageProcess.Packages
                topLevelPackage = PackageListUtil.GetTopLevelPackage(packages)
                if discoverFeatureList:
                    packageFilters.FeatureNameList = [
                        entry.Name
                        for entry in topLevelPackage.ResolvedAllUsedFeatures
                    ]

        customPackageFileFilter = None  # type: Optional[CustomPackageFileFilter]
        if not localToolConfig.ScanSource and not applyClangFormat and not applyClangTidy:
            Validate.ValidatePlatform(config, localToolConfig.PlatformName,
                                      packageFilters.FeatureNameList)
            if packageProcess is None:
                packageProcess = self.__CreatePackageProcess(
                    config, toolConfig.GetMinimalConfig(generator.CMakeConfig),
                    closestGenFilePath, localToolConfig.Recursive,
                    generatorContext.Platform, toolPackageNames)
            if not packageProcess.IsFullResolve or packages is None:
                # For now this requires a full resolve (but basically it only requires basic + files)
                packages = packageProcess.Resolve(generatorContext,
                                                  packageFilters,
                                                  applyClangTidy, True)

            topLevelPackage = PackageListUtil.GetTopLevelPackage(packages)
            RecipeBuilder.ValidateInstallationForPackages(
                config, config.SDKPath, generatorContext,
                topLevelPackage.ResolvedBuildOrder)
        else:
            if localToolConfig.File is not None:
                # Delay extension validation
                customPackageFileFilter = CustomPackageFileFilter(
                    localToolConfig.File)

        theTopLevelPackage = None  # type: Optional[Package]
        filteredPackageList = []  # type: List[Package]
        if applyClangTidy or applyClangFormat or localToolConfig.ScanSource:
            addExternals = applyClangTidy
            filteredPackageList, theTopLevelPackage = self.__PreparePackages(
                self.Log, localToolConfig, packageProcess, generatorContext,
                packageFilters, addExternals, packages, config.IsSDKBuild,
                applyClangTidy, config)
            if len(filteredPackageList) <= 0:
                self.Log.DoPrint("No supported packages left to process")
                return

        if applyClangTidy:
            self.__ApplyClangTidy(self.Log, toolConfig, localToolConfig,
                                  packageRecipeResultManager,
                                  theTopLevelPackage, filteredPackageList,
                                  generator, config, generatorContext,
                                  customPackageFileFilter)

        if applyClangFormat:
            self.__ApplyClangFormat(self.Log, toolConfig, localToolConfig,
                                    packageRecipeResultManager,
                                    filteredPackageList,
                                    customPackageFileFilter,
                                    generatorContext.CMakeConfig)

        # Scan source after 'format' to ensure we dont warn about stuff that has been fixed
        if localToolConfig.ScanSource:
            self.__ApplyScanSource(self.Log, localToolConfig,
                                   config.IsSDKBuild, config.DisableWrite,
                                   filteredPackageList,
                                   customPackageFileFilter)