<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"

	"v.io/lib/cmdline"
	"v.io/tools/lib/collect"
	"v.io/tools/lib/envutil"
	"v.io/tools/lib/util"
)

var (
	defaultDirPerm  = os.FileMode(0755)
	defaultFilePerm = os.FileMode(0644)
	knownProfiles   = map[string]struct{}{
		"arm":           struct{}{},
		"mobile":        struct{}{},
		"proximity":     struct{}{},
		"proximity-arm": struct{}{},
		"syncbase":      struct{}{},
		"third-party":   struct{}{},
		"web":           struct{}{},
	}
)

const (
	// Number of retries for profile setup.
	numRetries = 3
)

// cmdProfile represents the "v23 profile" command.
var cmdProfile = &cmdline.Command{
	Name:  "profile",
	Short: "Manage vanadium profiles",
	Long: `
To facilitate development across different platforms, vanadium defines
platform-independent profiles that map different platforms to a set
of libraries and tools that can be used for a factor of vanadium
development.
`,
	Children: []*cmdline.Command{cmdProfileList, cmdProfileSetup},
}

// cmdProfileList represents the "v23 profile list" command.
var cmdProfileList = &cmdline.Command{
	Run:   runProfileList,
	Name:  "list",
	Short: "List known vanadium profiles",
	Long:  "List known vanadium profiles.",
}

func runProfileList(command *cmdline.Command, _ []string) error {
	profiles := []string{}
	for p := range knownProfiles {
		profiles = append(profiles, p)
	}
	sort.Strings(profiles)
	for _, p := range profiles {
		fmt.Fprintf(command.Stdout(), "%s\n", p)
	}
	return nil
}

// cmdProfileSetup represents the "v23 profile setup" command.
var cmdProfileSetup = &cmdline.Command{
	Run:      runProfileSetup,
	Name:     "setup",
	Short:    "Set up the given vanadium profiles",
	Long:     "Set up the given vanadium profiles.",
	ArgsName: "<profiles>",
	ArgsLong: "<profiles> is a list of profiles to set up.",
}

func runProfileSetup(command *cmdline.Command, args []string) error {
	// Check that the profiles to be set up exist.
	for _, arg := range args {
		if _, ok := knownProfiles[arg]; !ok {
			return command.UsageErrorf("profile %v does not exist", arg)
		}
	}

	// Setup the profiles.
	ctx := util.NewContextFromCommand(command, !noColorFlag, dryRunFlag, true)
	for _, arg := range args {
		setupFn := func() error {
			var err error
			for i := 1; i <= numRetries; i++ {
				fmt.Fprintf(ctx.Stdout(), fmt.Sprintf("Attempt #%d\n", i))
				err = setup(ctx, runtime.GOOS, arg)
				if err == nil {
					return nil
				}
			}
			return err
		}
		if err := ctx.Run().Function(setupFn, fmt.Sprintf("Set up profile %q", arg)); err != nil {
			return err
		}
	}
	return nil
}

type unknownProfileErr string

func (e unknownProfileErr) Error() string {
	return fmt.Sprintf("unknown profile %q", e)
}

func reportNotImplemented(ctx *util.Context, os, profile string) {
	ctx.Run().Output([]string{fmt.Sprintf("profile %q is not implemented on %q", profile, os)})
}

func setup(ctx *util.Context, os, profile string) error {
	switch os {
	case "darwin":
		switch profile {
		case "third-party":
			return setupThirdPartyDarwin(ctx)
		case "web":
			return setupWebDarwin(ctx)
		default:
			reportNotImplemented(ctx, os, profile)
		}
	case "linux":
		switch profile {
		case "arm":
			return setupArmLinux(ctx)
		case "mobile":
			return setupMobileLinux(ctx)
		case "proximity":
			return setupProximityLinux(ctx)
		case "proximity-arm":
			return setupProximityArmLinux(ctx)
		case "syncbase":
			return setupSyncbaseLinux(ctx)
		case "web":
			return setupWebLinux(ctx)
		default:
			reportNotImplemented(ctx, os, profile)
		}
	default:
		reportNotImplemented(ctx, os, profile)
	}
	return nil
}

func atomicAction(ctx *util.Context, installFn func() error, dir, message string) error {
	atomicFn := func() error {
		if dir != "" && directoryExists(ctx, dir) {
			return nil
		}
		if err := installFn(); err != nil {
			if dir != "" {
				ctx.Run().RemoveAll(dir)
			}
			return err
		}
		return nil
	}
	return ctx.Run().Function(atomicFn, message)
}

func directoryExists(ctx *util.Context, dir string) bool {
	return ctx.Run().Command("test", "-d", dir) == nil
}

type androidPkg struct {
	name      string
	directory string
}

func installAndroidPkg(ctx *util.Context, sdkRoot string, pkg androidPkg) error {
	installPkgFn := func() error {
		// Identify all indexes that match the given package.<commit_msg>TBR: tools/v23: make atomic action more atomic; append suffix to thirdparty go/race test so they won't have duplicated names.<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"

	"v.io/lib/cmdline"
	"v.io/tools/lib/collect"
	"v.io/tools/lib/envutil"
	"v.io/tools/lib/util"
)

var (
	defaultDirPerm  = os.FileMode(0755)
	defaultFilePerm = os.FileMode(0644)
	knownProfiles   = map[string]struct{}{
		"arm":           struct{}{},
		"mobile":        struct{}{},
		"proximity":     struct{}{},
		"proximity-arm": struct{}{},
		"syncbase":      struct{}{},
		"third-party":   struct{}{},
		"web":           struct{}{},
	}
)

const (
	// Number of retries for profile setup.
	numRetries              = 3
	actionCompletedFileName = ".vanadium_action_completed"
)

// cmdProfile represents the "v23 profile" command.
var cmdProfile = &cmdline.Command{
	Name:  "profile",
	Short: "Manage vanadium profiles",
	Long: `
To facilitate development across different platforms, vanadium defines
platform-independent profiles that map different platforms to a set
of libraries and tools that can be used for a factor of vanadium
development.
`,
	Children: []*cmdline.Command{cmdProfileList, cmdProfileSetup},
}

// cmdProfileList represents the "v23 profile list" command.
var cmdProfileList = &cmdline.Command{
	Run:   runProfileList,
	Name:  "list",
	Short: "List known vanadium profiles",
	Long:  "List known vanadium profiles.",
}

func runProfileList(command *cmdline.Command, _ []string) error {
	profiles := []string{}
	for p := range knownProfiles {
		profiles = append(profiles, p)
	}
	sort.Strings(profiles)
	for _, p := range profiles {
		fmt.Fprintf(command.Stdout(), "%s\n", p)
	}
	return nil
}

// cmdProfileSetup represents the "v23 profile setup" command.
var cmdProfileSetup = &cmdline.Command{
	Run:      runProfileSetup,
	Name:     "setup",
	Short:    "Set up the given vanadium profiles",
	Long:     "Set up the given vanadium profiles.",
	ArgsName: "<profiles>",
	ArgsLong: "<profiles> is a list of profiles to set up.",
}

func runProfileSetup(command *cmdline.Command, args []string) error {
	// Check that the profiles to be set up exist.
	for _, arg := range args {
		if _, ok := knownProfiles[arg]; !ok {
			return command.UsageErrorf("profile %v does not exist", arg)
		}
	}

	// Setup the profiles.
	ctx := util.NewContextFromCommand(command, !noColorFlag, dryRunFlag, true)
	for _, arg := range args {
		setupFn := func() error {
			var err error
			for i := 1; i <= numRetries; i++ {
				fmt.Fprintf(ctx.Stdout(), fmt.Sprintf("Attempt #%d\n", i))
				err = setup(ctx, runtime.GOOS, arg)
				if err == nil {
					return nil
				}
			}
			return err
		}
		if err := ctx.Run().Function(setupFn, fmt.Sprintf("Set up profile %q", arg)); err != nil {
			return err
		}
	}
	return nil
}

type unknownProfileErr string

func (e unknownProfileErr) Error() string {
	return fmt.Sprintf("unknown profile %q", e)
}

func reportNotImplemented(ctx *util.Context, os, profile string) {
	ctx.Run().Output([]string{fmt.Sprintf("profile %q is not implemented on %q", profile, os)})
}

func setup(ctx *util.Context, os, profile string) error {
	switch os {
	case "darwin":
		switch profile {
		case "third-party":
			return setupThirdPartyDarwin(ctx)
		case "web":
			return setupWebDarwin(ctx)
		default:
			reportNotImplemented(ctx, os, profile)
		}
	case "linux":
		switch profile {
		case "arm":
			return setupArmLinux(ctx)
		case "mobile":
			return setupMobileLinux(ctx)
		case "proximity":
			return setupProximityLinux(ctx)
		case "proximity-arm":
			return setupProximityArmLinux(ctx)
		case "syncbase":
			return setupSyncbaseLinux(ctx)
		case "web":
			return setupWebLinux(ctx)
		default:
			reportNotImplemented(ctx, os, profile)
		}
	default:
		reportNotImplemented(ctx, os, profile)
	}
	return nil
}

func atomicAction(ctx *util.Context, installFn func() error, dir, message string) error {
	atomicFn := func() error {
		actionCompletedFile := filepath.Join(dir, actionCompletedFileName)
		if dir != "" && directoryExists(ctx, dir) {
			// If the dir exists but the actionCompletedFile doesn't, then it means
			// the previous action didn't finish.
			// Remove the dir so we can perform the action again.
			if !fileExists(ctx, actionCompletedFile) {
				ctx.Run().RemoveAll(dir)
			} else {
				return nil
			}
		}
		if err := installFn(); err != nil {
			if dir != "" {
				ctx.Run().RemoveAll(dir)
			}
			return err
		}
		if err := ctx.Run().WriteFile(actionCompletedFile, []byte("completed"), 0644); err != nil {
			return err
		}
		return nil
	}
	return ctx.Run().Function(atomicFn, message)
}

func directoryExists(ctx *util.Context, dir string) bool {
	return ctx.Run().Command("test", "-d", dir) == nil
}

func fileExists(ctx *util.Context, file string) bool {
	return ctx.Run().Command("test", "-f", file) == nil
}

type androidPkg struct {
	name      string
	directory string
}

func installAndroidPkg(ctx *util.Context, sdkRoot string, pkg androidPkg) error {
	installPkgFn := func() error {
		// Identify all indexes that match the given package.
{'fraction_non_alphanumeric': 0.1339369341242585, 'fraction_numerical': 0.003954625871578728, 'mean_word_length': 3.7527200791295745, 'pattern_counts': {'":': 34, '<': 9, '<?xml version=': 0, '>': 7, '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': '863674', 'n_tokens_mistral': 3479, 'n_tokens_neox': 3187, 'n_words': 937}