func TestObjFileIsUpToDateObjMissing(t *testing.T) {
	sourceFile := tempFile(t, "source")
	defer os.RemoveAll(sourceFile)

	upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, "", "")
	NoError(t, err)
	require.False(t, upToDate)
}
func TestObjFileIsUpToDateObjNewer(t *testing.T) {
	sourceFile := tempFile(t, "source")
	defer os.RemoveAll(sourceFile)

	sleep(t)

	objFile := tempFile(t, "obj")
	defer os.RemoveAll(objFile)
	depFile := tempFile(t, "dep")
	defer os.RemoveAll(depFile)

	upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
	NoError(t, err)
	require.True(t, upToDate)
}
func TestObjFileIsUpToDateDepIsOlder(t *testing.T) {
	sourceFile := tempFile(t, "source")
	defer os.RemoveAll(sourceFile)

	headerFile := tempFile(t, "header")
	defer os.RemoveAll(headerFile)

	sleep(t)

	objFile := tempFile(t, "obj")
	defer os.RemoveAll(objFile)
	depFile := tempFile(t, "dep")
	defer os.RemoveAll(depFile)

	utils.WriteFile(depFile, objFile+": \\\n\t"+sourceFile+" \\\n\t"+headerFile)

	upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
	NoError(t, err)
	require.True(t, upToDate)
}
func TestObjFileIsUpToDateDepIsWrong(t *testing.T) {
	sourceFile := tempFile(t, "source")
	defer os.RemoveAll(sourceFile)

	sleep(t)

	objFile := tempFile(t, "obj")
	defer os.RemoveAll(objFile)
	depFile := tempFile(t, "dep")
	defer os.RemoveAll(depFile)

	sleep(t)

	headerFile := tempFile(t, "header")
	defer os.RemoveAll(headerFile)

	ioutil.WriteFile(depFile, []byte(sourceFile+": \\\n\t"+sourceFile+" \\\n\t"+headerFile), os.FileMode(0644))

	upToDate, err := builder_utils.ObjFileIsUpToDate(sourceFile, objFile, depFile)
	NoError(t, err)
	require.False(t, upToDate)
}
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
	}
}