func findIncludesUntilDone(context map[string]interface{}, sourceFilePath string) error {
	targetFilePath := utils.NULLFile()
	importedLibraries := context[constants.CTX_IMPORTED_LIBRARIES].([]*types.Library)
	done := false
	for !done {
		commands := []types.Command{
			&GCCPreprocRunnerForDiscoveringIncludes{SourceFilePath: sourceFilePath, TargetFilePath: targetFilePath},
			&IncludesFinderWithRegExp{ContextField: constants.CTX_GCC_MINUS_E_SOURCE},
			&IncludesToIncludeFolders{},
		}
		for _, command := range commands {
			err := runCommand(context, command)
			if err != nil {
				return utils.WrapError(err)
			}
		}
		if len(context[constants.CTX_INCLUDES_JUST_FOUND].([]string)) == 0 {
			done = true
		} else if len(context[constants.CTX_IMPORTED_LIBRARIES].([]*types.Library)) == len(importedLibraries) {
			err := runCommand(context, &GCCPreprocRunner{TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E})
			return utils.WrapError(err)
		}
		importedLibraries = context[constants.CTX_IMPORTED_LIBRARIES].([]*types.Library)
		context[constants.CTX_INCLUDES_JUST_FOUND] = []string{}
	}
	return nil
}
func prepareGCCPreprocRecipeProperties(context map[string]interface{}, sourceFilePath string, targetFilePath string) (props.PropertiesMap, string, error) {
	if targetFilePath != utils.NULLFile() {
		preprocPath := context[constants.CTX_PREPROC_PATH].(string)
		err := utils.EnsureFolderExists(preprocPath)
		if err != nil {
			return nil, "", utils.WrapError(err)
		}
		targetFilePath = filepath.Join(preprocPath, targetFilePath)
	}

	properties := make(props.PropertiesMap)
	if p, ok := context[constants.CTX_BUILD_PROPERTIES]; ok {
		properties = p.(props.PropertiesMap).Clone()
	}

	properties[constants.BUILD_PROPERTIES_SOURCE_FILE] = sourceFilePath
	properties[constants.BUILD_PROPERTIES_PREPROCESSED_FILE_PATH] = targetFilePath

	includes := context[constants.CTX_INCLUDE_FOLDERS].([]string)
	includes = utils.Map(includes, utils.WrapWithHyphenI)
	properties[constants.BUILD_PROPERTIES_INCLUDES] = strings.Join(includes, constants.SPACE)
	builder_utils.RemoveHyphenMDDFlagFromGCCCommandLine(properties)

	return properties, targetFilePath, nil
}
func findIncludesUntilDone(ctx *types.Context, sourceFilePath string) error {
	targetFilePath := utils.NULLFile()
	importedLibraries := ctx.ImportedLibraries
	done := false
	for !done {
		commands := []types.Command{
			&GCCPreprocRunnerForDiscoveringIncludes{SourceFilePath: sourceFilePath, TargetFilePath: targetFilePath},
			&IncludesFinderWithRegExp{Source: &ctx.SourceGccMinusE},
			&IncludesToIncludeFolders{},
		}
		for _, command := range commands {
			err := runCommand(ctx, command)
			if err != nil {
				return i18n.WrapError(err)
			}
		}
		if len(ctx.IncludesJustFound) == 0 {
			done = true
		} else if len(ctx.ImportedLibraries) == len(importedLibraries) {
			err := runCommand(ctx, &GCCPreprocRunner{TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E})
			return i18n.WrapError(err)
		}
		importedLibraries = ctx.ImportedLibraries
		ctx.IncludesJustFound = []string{}
	}
	return nil
}
func prepareGCCPreprocRecipeProperties(ctx *types.Context, sourceFilePath string, targetFilePath string, includes []string) (properties.Map, string, error) {
	if targetFilePath != utils.NULLFile() {
		preprocPath := ctx.PreprocPath
		err := utils.EnsureFolderExists(preprocPath)
		if err != nil {
			return nil, "", i18n.WrapError(err)
		}
		targetFilePath = filepath.Join(preprocPath, targetFilePath)
	}

	properties := ctx.BuildProperties.Clone()
	properties[constants.BUILD_PROPERTIES_SOURCE_FILE] = sourceFilePath
	properties[constants.BUILD_PROPERTIES_PREPROCESSED_FILE_PATH] = targetFilePath

	includes = utils.Map(includes, utils.WrapWithHyphenI)
	properties[constants.BUILD_PROPERTIES_INCLUDES] = strings.Join(includes, constants.SPACE)
	builder_utils.RemoveHyphenMDDFlagFromGCCCommandLine(properties)

	return properties, targetFilePath, nil
}
func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile types.SourceFile) error {
	sourcePath := sourceFile.SourcePath(ctx)
	targetFilePath := utils.NULLFile()

	// TODO: This should perhaps also compare against the
	// include.cache file timestamp. Now, it only checks if the file
	// changed after the object file was generated, but if it
	// changed between generating the cache and the object file,
	// this could show the file as unchanged when it really is
	// changed. Changing files during a build isn't really
	// supported, but any problems from it should at least be
	// resolved when doing another build, which is not currently the
	// case.
	// TODO: This reads the dependency file, but the actual building
	// does it again. Should the result be somehow cached? Perhaps
	// remove the object file if it is found to be stale?
	unchanged, err := builder_utils.ObjFileIsUpToDate(sourcePath, sourceFile.ObjectPath(ctx), sourceFile.DepfilePath(ctx))
	if err != nil {
		return i18n.WrapError(err)
	}

	first := true
	for {
		var include string
		cache.ExpectFile(sourcePath)

		includes := ctx.IncludeFolders
		if library, ok := sourceFile.Origin.(*types.Library); ok && library.UtilityFolder != "" {
			includes = append(includes, library.UtilityFolder)
		}
		if unchanged && cache.valid {
			include = cache.Next().Include
			if first && ctx.Verbose {
				ctx.GetLogger().Println(constants.LOG_LEVEL_INFO, constants.MSG_USING_CACHED_INCLUDES, sourcePath)
			}
		} else {
			commands := []types.Command{
				&GCCPreprocRunnerForDiscoveringIncludes{SourceFilePath: sourcePath, TargetFilePath: targetFilePath, Includes: includes},
				&IncludesFinderWithRegExp{Source: &ctx.SourceGccMinusE},
			}
			for _, command := range commands {
				err := runCommand(ctx, command)
				if err != nil {
					return i18n.WrapError(err)
				}
			}
			include = ctx.IncludeJustFound
		}

		if include == "" {
			// No missing includes found, we're done
			cache.ExpectEntry(sourcePath, "", "")
			return nil
		}

		library := ResolveLibrary(ctx, include)
		if library == nil {
			// Library could not be resolved, show error
			err := runCommand(ctx, &GCCPreprocRunner{SourceFilePath: sourcePath, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E, Includes: includes})
			return i18n.WrapError(err)
		}

		// Add this library to the list of libraries, the
		// include path and queue its source files for further
		// include scanning
		ctx.ImportedLibraries = append(ctx.ImportedLibraries, library)
		appendIncludeFolder(ctx, cache, sourcePath, include, library.SrcFolder)
		sourceFolders := types.LibraryToSourceFolder(library)
		for _, sourceFolder := range sourceFolders {
			queueSourceFilesFromFolder(ctx, ctx.CollectedSourceFiles, library, sourceFolder.Folder, sourceFolder.Recurse)
		}
		first = false
	}
}