<commit_before>package common

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"go.skia.org/infra/fuzzer/go/config"
	"go.skia.org/infra/go/buildskia"
	"go.skia.org/infra/go/fileutil"
	"go.skia.org/infra/go/git/gitinfo"
	"go.skia.org/infra/go/sklog"
)

// BuildClangHarness builds the test harness for fuzzing using clang, pulling it from the executable
// cache if possible.  It returns the path to the executable (which should be copied somewhere else)
// and any error.
func BuildClangHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s clang harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
	}
	return buildOrGetCachedHarness(ctx, "clang", buildType, isClean, buildArgs)
}

// BuildASANHarness builds the test harness for fuzzing using clang and AddressSanitizer, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildASANHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s ASAN harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
		`sanitize="address"`, // No UBSAN, to avoid noise.
	}
	return buildOrGetCachedHarness(ctx, "asan", buildType, isClean, buildArgs)
}

// BuildFuzzingHarness builds the test harness for fuzzing using afl-instrumented clang, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildFuzzingHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s fuzzing harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast")),
		fmt.Sprintf("cxx=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast++")),
	}

	return buildOrGetCachedHarness(ctx, "afl-instrumented", buildType, isClean, buildArgs)
}

// buildOrGetCachedHarness first looks into the ExecutableCache for a already built binary.  If it
// cannot find one, it triggers a build and puts it in the cache.  The cache is structured like:
// [ExecutableCachePath]/[skia-hash]/[buildType]/[buildname]
// It returns the path to the executable (which should be copied somewhere else) and any error.
// buildName is a human friendly name for this build type. buildType is Release, Debug, etc,
// buildName and buildType work together to identify a unique build (in the eyes of the cache, at
// least).  isClean is whether the build output directory should be cleared before making a new
// build.  buildArgs are the arguments passed to GN.
func buildOrGetCachedHarness(ctx context.Context, buildName string, buildType buildskia.ReleaseType, isClean bool, buildArgs []string) (string, error) {
	if buildType == buildskia.RELEASE_BUILD {
		buildArgs = append(buildArgs, "is_debug=false")
	}
	// This makes crashing because we ran out of memory or because someone called SK_ABORT turn
	// into an exit(1), so we don't count it as a "crash".
	buildArgs = append(buildArgs, `extra_cflags=["-DIS_FUZZING", "-DIS_FUZZING_AFL"]`)
	// System freetype has many MSAN-like bugs, which can throw off our fuzzer. Build our own
	// (newer) freetype to minimize these.
	buildArgs = append(buildArgs, "skia_use_system_freetype2=false")

	d := filepath.Join(config.Common.SkiaRoot, "skia")
	gi, err := gitinfo.NewGitInfo(ctx, d, false, false)
	if err != nil {
		return "", fmt.Errorf("Could not locate git info about Skia Root %s: %s", d, err)
	}
	hashes := gi.LastN(ctx, 1)
	if len(hashes) != 1 {
		return "", fmt.Errorf("Could not get last git hash, instead got %q", hashes)
	}

	cache := filepath.Join(config.Common.ExecutableCachePath, hashes[0], string(buildType))
	cache, err = fileutil.EnsureDirExists(cache)
	if err != nil {
		return "", fmt.Errorf("Could not create cache dir %s: %s", cache, err)
	}

	cachedFile := filepath.Join(cache, buildName)
	if info, err := os.Stat(cachedFile); err != nil {
		if os.IsNotExist(err) {
			sklog.Infof("Did not find %s %s build for revision %s in cache.  Going to build it.", buildName, buildType, hashes[0])
			if builtExePath, err := buildHarness(ctx, buildType, isClean, buildArgs); err != nil {
				return "", fmt.Errorf("There was a problem building: %s", err)
			} else {
				return cachedFile, fileutil.CopyExecutable(builtExePath, cachedFile)
			}
		}<commit_msg>Update flag to what actually landed<commit_after>package common

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"go.skia.org/infra/fuzzer/go/config"
	"go.skia.org/infra/go/buildskia"
	"go.skia.org/infra/go/fileutil"
	"go.skia.org/infra/go/git/gitinfo"
	"go.skia.org/infra/go/sklog"
)

// BuildClangHarness builds the test harness for fuzzing using clang, pulling it from the executable
// cache if possible.  It returns the path to the executable (which should be copied somewhere else)
// and any error.
func BuildClangHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s clang harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
	}
	return buildOrGetCachedHarness(ctx, "clang", buildType, isClean, buildArgs)
}

// BuildASANHarness builds the test harness for fuzzing using clang and AddressSanitizer, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildASANHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s ASAN harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", config.Common.ClangPath),
		fmt.Sprintf("cxx=%q", config.Common.ClangPlusPlusPath),
		`sanitize="address"`, // No UBSAN, to avoid noise.
	}
	return buildOrGetCachedHarness(ctx, "asan", buildType, isClean, buildArgs)
}

// BuildFuzzingHarness builds the test harness for fuzzing using afl-instrumented clang, pulling it
// from the executable cache if possible.  It returns the path to the executable (which should be
// copied somewhere else) and any error.
func BuildFuzzingHarness(ctx context.Context, buildType buildskia.ReleaseType, isClean bool) (string, error) {
	sklog.Infof("Building %s fuzzing harness, or fetching from cache", buildType)
	buildArgs := []string{
		fmt.Sprintf("cc=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast")),
		fmt.Sprintf("cxx=%q", filepath.Join(config.Generator.AflRoot, "afl-clang-fast++")),
	}

	return buildOrGetCachedHarness(ctx, "afl-instrumented", buildType, isClean, buildArgs)
}

// buildOrGetCachedHarness first looks into the ExecutableCache for a already built binary.  If it
// cannot find one, it triggers a build and puts it in the cache.  The cache is structured like:
// [ExecutableCachePath]/[skia-hash]/[buildType]/[buildname]
// It returns the path to the executable (which should be copied somewhere else) and any error.
// buildName is a human friendly name for this build type. buildType is Release, Debug, etc,
// buildName and buildType work together to identify a unique build (in the eyes of the cache, at
// least).  isClean is whether the build output directory should be cleared before making a new
// build.  buildArgs are the arguments passed to GN.
func buildOrGetCachedHarness(ctx context.Context, buildName string, buildType buildskia.ReleaseType, isClean bool, buildArgs []string) (string, error) {
	if buildType == buildskia.RELEASE_BUILD {
		buildArgs = append(buildArgs, "is_debug=false")
	}
	// This makes crashing because we ran out of memory or because someone called SK_ABORT turn
	// into an exit(1), so we don't count it as a "crash".
	buildArgs = append(buildArgs, `extra_cflags=["-DIS_FUZZING", "-DIS_FUZZING_WITH_AFL"]`)
	// System freetype has many MSAN-like bugs, which can throw off our fuzzer. Build our own
	// (newer) freetype to minimize these.
	buildArgs = append(buildArgs, "skia_use_system_freetype2=false")

	d := filepath.Join(config.Common.SkiaRoot, "skia")
	gi, err := gitinfo.NewGitInfo(ctx, d, false, false)
	if err != nil {
		return "", fmt.Errorf("Could not locate git info about Skia Root %s: %s", d, err)
	}
	hashes := gi.LastN(ctx, 1)
	if len(hashes) != 1 {
		return "", fmt.Errorf("Could not get last git hash, instead got %q", hashes)
	}

	cache := filepath.Join(config.Common.ExecutableCachePath, hashes[0], string(buildType))
	cache, err = fileutil.EnsureDirExists(cache)
	if err != nil {
		return "", fmt.Errorf("Could not create cache dir %s: %s", cache, err)
	}

	cachedFile := filepath.Join(cache, buildName)
	if info, err := os.Stat(cachedFile); err != nil {
		if os.IsNotExist(err) {
			sklog.Infof("Did not find %s %s build for revision %s in cache.  Going to build it.", buildName, buildType, hashes[0])
			if builtExePath, err := buildHarness(ctx, buildType, isClean, buildArgs); err != nil {
				return "", fmt.Errorf("There was a problem building: %s", err)
			} else {
				return cachedFile, fileutil.CopyExecutable(builtExePath, cachedFile)
			}
		}
{'fraction_non_alphanumeric': 0.11685440572691862, 'fraction_numerical': 0.0012632908727234445, 'mean_word_length': 4.688622754491018, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '5806572', 'n_tokens_mistral': 3180, 'n_tokens_neox': 3054, 'n_words': 1007}