func TestSetupBuildPropertiesWithMissingPropsFromParentPlatformTxtFiles(t *testing.T) {
	DownloadCoresAndToolsAndLibraries(t)

	context := make(map[string]interface{})

	buildPath := SetupBuildPath(t, context)
	defer os.RemoveAll(buildPath)

	context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = "10600"
	context[constants.CTX_HARDWARE_FOLDERS] = []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware", "user_hardware"}
	context[constants.CTX_TOOLS_FOLDERS] = []string{"downloaded_tools", "./tools_builtin"}
	context[constants.CTX_FQBN] = "my_avr_platform:avr:custom_yun"
	context[constants.CTX_SKETCH_LOCATION] = filepath.Join("sketch1", "sketch.ino")

	commands := []types.Command{
		&builder.SetupHumanLoggerIfMissing{},
		&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
	}

	for _, command := range commands {
		err := command.Run(context)
		NoError(t, err)
	}

	buildProperties := context[constants.CTX_BUILD_PROPERTIES].(props.PropertiesMap)

	require.Equal(t, "ARDUINO", buildProperties[constants.BUILD_PROPERTIES_SOFTWARE])

	require.Equal(t, "custom_yun", buildProperties[constants.ID])
	require.Equal(t, "Arduino Yún", buildProperties["name"])
	require.Equal(t, "0x2341", buildProperties["vid.0"])
	require.Equal(t, "\"{compiler.path}{compiler.c.cmd}\" {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} \"{source_file}\" -o \"{object_file}\"", buildProperties["recipe.c.o.pattern"])
	require.Equal(t, "{path}/etc/avrdude.conf", buildProperties["tools.avrdude.config.path"])

	coanProps := buildProperties.SubTree(constants.BUILD_PROPERTIES_TOOLS_KEY).SubTree(constants.COAN)
	require.Equal(t, "{path}/coan", coanProps["cmd.path"])
	require.Equal(t, "\"{cmd.path}\" source -m -E -P -kb {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} \"{source_file}\"", coanProps[constants.BUILD_PROPERTIES_PATTERN])

	require.Equal(t, Abs(t, "user_hardware/my_avr_platform/avr"), buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH])
	require.Equal(t, Abs(t, "user_hardware/my_avr_platform"), buildProperties[constants.BUILD_PROPERTIES_RUNTIME_HARDWARE_PATH])
	require.Equal(t, "10600", buildProperties[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION])
	require.NotEmpty(t, buildProperties[constants.BUILD_PROPERTIES_RUNTIME_OS])

	require.Equal(t, Abs(t, "./downloaded_tools/arm-none-eabi-gcc/4.8.3-2014q1"), buildProperties["runtime.tools.arm-none-eabi-gcc.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/arm-none-eabi-gcc/4.8.3-2014q1"), buildProperties["runtime.tools.arm-none-eabi-gcc-4.8.3-2014q1.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/bossac/1.6.1-arduino"), buildProperties["runtime.tools.bossac-1.6.1-arduino.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/bossac/1.5-arduino"), buildProperties["runtime.tools.bossac-1.5-arduino.path"])
	require.True(t, buildProperties["runtime.tools.bossac.path"] == Abs(t, "./downloaded_tools/bossac/1.6.1-arduino") || buildProperties["runtime.tools.bossac.path"] == Abs(t, "./downloaded_tools/bossac/1.5-arduino"))
	require.Equal(t, Abs(t, "./downloaded_tools/avrdude/6.0.1-arduino5"), buildProperties["runtime.tools.avrdude.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/avrdude/6.0.1-arduino5"), buildProperties["runtime.tools.avrdude-6.0.1-arduino5.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/avr-gcc/4.8.1-arduino5"), buildProperties["runtime.tools.avr-gcc.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/avr-gcc/4.8.1-arduino5"), buildProperties["runtime.tools.avr-gcc-4.8.1-arduino5.path"])

	require.Equal(t, Abs(t, "sketch1"), buildProperties[constants.BUILD_PROPERTIES_SOURCE_PATH])

	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_UTC))
	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_LOCAL))
	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_ZONE))
	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_DST))
}
func TestSetupBuildProperties(t *testing.T) {
	DownloadCoresAndToolsAndLibraries(t)

	ctx := &types.Context{
		HardwareFolders:   []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware", "user_hardware"},
		ToolsFolders:      []string{"downloaded_tools", "./tools_builtin"},
		SketchLocation:    filepath.Join("sketch1", "sketch.ino"),
		FQBN:              "arduino:avr:uno",
		ArduinoAPIVersion: "10600",
	}

	buildPath := SetupBuildPath(t, ctx)
	defer os.RemoveAll(buildPath)

	commands := []types.Command{
		&builder.AddAdditionalEntriesToContext{},
		&builder.HardwareLoader{},
		&builder.ToolsLoader{},
		&builder.TargetBoardResolver{},
		&builder.SketchLoader{},
		&builder.SetupBuildProperties{},
	}

	for _, command := range commands {
		err := command.Run(ctx)
		NoError(t, err)
	}

	buildProperties := ctx.BuildProperties

	require.Equal(t, "ARDUINO", buildProperties[constants.BUILD_PROPERTIES_SOFTWARE])

	require.Equal(t, "uno", buildProperties[constants.ID])
	require.Equal(t, "Arduino/Genuino Uno", buildProperties["name"])
	require.Equal(t, "0x2341", buildProperties["vid.0"])
	require.Equal(t, "\"{compiler.path}{compiler.c.cmd}\" {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} \"{source_file}\" -o \"{object_file}\"", buildProperties["recipe.c.o.pattern"])
	require.Equal(t, "{path}/etc/avrdude.conf", buildProperties["tools.avrdude.config.path"])

	require.Equal(t, Abs(t, "downloaded_hardware/arduino/avr"), buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH])
	require.Equal(t, Abs(t, "downloaded_hardware/arduino"), buildProperties[constants.BUILD_PROPERTIES_RUNTIME_HARDWARE_PATH])
	require.Equal(t, "10600", buildProperties[constants.BUILD_PROPERTIES_RUNTIME_IDE_VERSION])
	require.NotEmpty(t, buildProperties[constants.BUILD_PROPERTIES_RUNTIME_OS])

	require.Equal(t, Abs(t, "./downloaded_tools/arm-none-eabi-gcc/4.8.3-2014q1"), buildProperties["runtime.tools.arm-none-eabi-gcc.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/arm-none-eabi-gcc/4.8.3-2014q1"), buildProperties["runtime.tools.arm-none-eabi-gcc-4.8.3-2014q1.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/bossac/1.6.1-arduino"), buildProperties["runtime.tools.bossac-1.6.1-arduino.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/bossac/1.5-arduino"), buildProperties["runtime.tools.bossac-1.5-arduino.path"])
	require.True(t, buildProperties["runtime.tools.bossac.path"] == Abs(t, "./downloaded_tools/bossac/1.6.1-arduino") || buildProperties["runtime.tools.bossac.path"] == Abs(t, "./downloaded_tools/bossac/1.5-arduino"))
	require.Equal(t, Abs(t, "./downloaded_tools/avrdude/6.0.1-arduino5"), buildProperties["runtime.tools.avrdude.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/avrdude/6.0.1-arduino5"), buildProperties["runtime.tools.avrdude-6.0.1-arduino5.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/avr-gcc/4.8.1-arduino5"), buildProperties["runtime.tools.avr-gcc.path"])
	require.Equal(t, Abs(t, "./downloaded_tools/avr-gcc/4.8.1-arduino5"), buildProperties["runtime.tools.avr-gcc-4.8.1-arduino5.path"])

	require.Equal(t, Abs(t, "sketch1"), buildProperties[constants.BUILD_PROPERTIES_SOURCE_PATH])

	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_UTC))
	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_LOCAL))
	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_ZONE))
	require.True(t, utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_EXTRA_TIME_DST))
}
func (s *MergeSketchWithBootloader) Run(context map[string]interface{}) error {
	buildProperties := context[constants.CTX_BUILD_PROPERTIES].(map[string]string)
	if !utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_BOOTLOADER_NOBLINK) && !utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_BOOTLOADER_FILE) {
		return nil
	}

	buildPath := context[constants.CTX_BUILD_PATH].(string)
	sketch := context[constants.CTX_SKETCH].(*types.Sketch)
	sketchFileName := filepath.Base(sketch.MainFile.Name)
	logger := context[constants.CTX_LOGGER].(i18n.Logger)

	sketchInBuildPath := filepath.Join(buildPath, sketchFileName+".hex")
	sketchInSubfolder := filepath.Join(buildPath, constants.FOLDER_SKETCH, sketchFileName+".hex")

	builtSketchPath := constants.EMPTY_STRING
	if _, err := os.Stat(sketchInBuildPath); err == nil {
		builtSketchPath = sketchInBuildPath
	} else if _, err := os.Stat(sketchInSubfolder); err == nil {
		builtSketchPath = sketchInSubfolder
	} else {
		return nil
	}

	bootloader := constants.EMPTY_STRING
	if utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_BOOTLOADER_NOBLINK) {
		bootloader = buildProperties[constants.BUILD_PROPERTIES_BOOTLOADER_NOBLINK]
	} else {
		bootloader = buildProperties[constants.BUILD_PROPERTIES_BOOTLOADER_FILE]
	}
	bootloader = props.ExpandPropsInString(buildProperties, bootloader)

	bootloaderPath := filepath.Join(buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH], constants.FOLDER_BOOTLOADERS, bootloader)
	if _, err := os.Stat(bootloaderPath); err != nil {
		logger.Fprintln(os.Stdout, constants.LOG_LEVEL_WARN, constants.MSG_BOOTLOADER_FILE_MISSING, bootloaderPath)
		return nil
	}

	mergedSketchPath := filepath.Join(filepath.Dir(builtSketchPath), sketchFileName+".with_bootloader.hex")

	err := merge(builtSketchPath, bootloaderPath, mergedSketchPath)

	return err
}
func (s *SetupBuildProperties) Run(context map[string]interface{}) error {
	packages := context[constants.CTX_HARDWARE].(map[string]*types.Package)

	targetPlatform := context[constants.CTX_TARGET_PLATFORM].(*types.Platform)
	actualPlatform := context[constants.CTX_ACTUAL_PLATFORM].(*types.Platform)
	targetBoard := context[constants.CTX_TARGET_BOARD].(*types.Board)

	buildProperties := make(map[string]string)
	utils.MergeMapsOfStrings(buildProperties, actualPlatform.Properties)
	utils.MergeMapsOfStrings(buildProperties, targetPlatform.Properties)
	utils.MergeMapsOfStrings(buildProperties, targetBoard.Properties)

	if utils.MapHas(context, constants.CTX_BUILD_PATH) {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_PATH] = context[constants.CTX_BUILD_PATH].(string)
	}
	if utils.MapHas(context, constants.CTX_SKETCH) {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_PROJECT_NAME] = filepath.Base(context[constants.CTX_SKETCH].(*types.Sketch).MainFile.Name)
	}
	buildProperties[constants.BUILD_PROPERTIES_BUILD_ARCH] = strings.ToUpper(targetPlatform.PlatformId)

	if buildProperties[constants.BUILD_PROPERTIES_COMPILER_PATH] == constants.EMPTY_STRING {
		return utils.Errorf(context, constants.MSG_MISSING_COMPILER_PATH)
	}

	buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE] = context[constants.CTX_BUILD_CORE].(string)
	buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE_PATH] = filepath.Join(actualPlatform.Folder, constants.FOLDER_CORES, buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE])
	buildProperties[constants.BUILD_PROPERTIES_BUILD_SYSTEM_PATH] = filepath.Join(actualPlatform.Folder, constants.FOLDER_SYSTEM)
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH] = actualPlatform.Folder
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_HARDWARE_PATH] = filepath.Join(actualPlatform.Folder, "..")
	buildProperties[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION].(string)
	buildProperties[constants.IDE_VERSION] = context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION].(string)
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_OS] = utils.PrettyOSName()

	variant := buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT]
	if variant == constants.EMPTY_STRING {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT_PATH] = constants.EMPTY_STRING
	} else {
		var variantPlatform *types.Platform
		variantParts := strings.Split(variant, ":")
		if len(variantParts) > 1 {
			variantPlatform = packages[variantParts[0]].Platforms[targetPlatform.PlatformId]
			variant = variantParts[1]
		} else {
			variantPlatform = targetPlatform
		}
		buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT_PATH] = filepath.Join(variantPlatform.Folder, constants.FOLDER_VARIANTS, variant)
	}

	tools := context[constants.CTX_TOOLS].([]*types.Tool)
	for _, tool := range tools {
		buildProperties[constants.BUILD_PROPERTIES_RUNTIME_TOOLS_PREFIX+tool.Name+constants.BUILD_PROPERTIES_RUNTIME_TOOLS_SUFFIX] = tool.Folder
		buildProperties[constants.BUILD_PROPERTIES_RUNTIME_TOOLS_PREFIX+tool.Name+"-"+tool.Version+constants.BUILD_PROPERTIES_RUNTIME_TOOLS_SUFFIX] = tool.Folder
	}

	if !utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_SOFTWARE) {
		buildProperties[constants.BUILD_PROPERTIES_SOFTWARE] = DEFAULT_SOFTWARE
	}

	if utils.MapHas(context, constants.CTX_SKETCH_LOCATION) {
		sourcePath, err := filepath.Abs(context[constants.CTX_SKETCH_LOCATION].(string))
		if err != nil {
			return err
		}
		buildProperties[constants.BUILD_PROPERTIES_SOURCE_PATH] = sourcePath
	}

	context[constants.CTX_BUILD_PROPERTIES] = buildProperties

	return nil
}
func (s *SetupBuildProperties) Run(context map[string]interface{}) error {
	packages := context[constants.CTX_HARDWARE].(*types.Packages)

	targetPlatform := context[constants.CTX_TARGET_PLATFORM].(*types.Platform)
	actualPlatform := context[constants.CTX_ACTUAL_PLATFORM].(*types.Platform)
	targetBoard := context[constants.CTX_TARGET_BOARD].(*types.Board)

	buildProperties := make(props.PropertiesMap)
	buildProperties.Merge(actualPlatform.Properties)
	buildProperties.Merge(targetPlatform.Properties)
	buildProperties.Merge(targetBoard.Properties)

	if utils.MapHas(context, constants.CTX_BUILD_PATH) {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_PATH] = context[constants.CTX_BUILD_PATH].(string)
	}
	if utils.MapHas(context, constants.CTX_SKETCH) {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_PROJECT_NAME] = filepath.Base(context[constants.CTX_SKETCH].(*types.Sketch).MainFile.Name)
	}
	buildProperties[constants.BUILD_PROPERTIES_BUILD_ARCH] = strings.ToUpper(targetPlatform.PlatformId)

	buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE] = context[constants.CTX_BUILD_CORE].(string)
	buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE_PATH] = filepath.Join(actualPlatform.Folder, constants.FOLDER_CORES, buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE])
	buildProperties[constants.BUILD_PROPERTIES_BUILD_SYSTEM_PATH] = filepath.Join(actualPlatform.Folder, constants.FOLDER_SYSTEM)
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH] = targetPlatform.Folder
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_HARDWARE_PATH] = filepath.Join(targetPlatform.Folder, "..")
	buildProperties[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION].(string)
	buildProperties[constants.IDE_VERSION] = context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION].(string)
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_OS] = utils.PrettyOSName()

	variant := buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT]
	if variant == constants.EMPTY_STRING {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT_PATH] = constants.EMPTY_STRING
	} else {
		var variantPlatform *types.Platform
		variantParts := strings.Split(variant, ":")
		if len(variantParts) > 1 {
			variantPlatform = packages.Packages[variantParts[0]].Platforms[targetPlatform.PlatformId]
			variant = variantParts[1]
		} else {
			variantPlatform = targetPlatform
		}
		buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT_PATH] = filepath.Join(variantPlatform.Folder, constants.FOLDER_VARIANTS, variant)
	}

	tools := context[constants.CTX_TOOLS].([]*types.Tool)
	for _, tool := range tools {
		buildProperties[constants.BUILD_PROPERTIES_RUNTIME_TOOLS_PREFIX+tool.Name+constants.BUILD_PROPERTIES_RUNTIME_TOOLS_SUFFIX] = tool.Folder
		buildProperties[constants.BUILD_PROPERTIES_RUNTIME_TOOLS_PREFIX+tool.Name+"-"+tool.Version+constants.BUILD_PROPERTIES_RUNTIME_TOOLS_SUFFIX] = tool.Folder
	}

	if !utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_SOFTWARE) {
		buildProperties[constants.BUILD_PROPERTIES_SOFTWARE] = DEFAULT_SOFTWARE
	}

	if utils.MapHas(context, constants.CTX_SKETCH_LOCATION) {
		sourcePath, err := filepath.Abs(context[constants.CTX_SKETCH_LOCATION].(string))
		if err != nil {
			return err
		}
		sourcePath = filepath.Dir(sourcePath)
		buildProperties[constants.BUILD_PROPERTIES_SOURCE_PATH] = sourcePath
	}

	now := time.Now()
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_UTC] = strconv.FormatInt(now.Unix(), 10)
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_LOCAL] = strconv.FormatInt(utils.LocalUnix(now), 10)
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_ZONE] = strconv.Itoa(utils.TimezoneOffset())
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_DST] = strconv.Itoa(utils.DaylightSavingsOffset(now))

	context[constants.CTX_BUILD_PROPERTIES] = buildProperties

	return nil
}
示例#6
0
func main() {
	flag.Parse()

	if *versionFlag {
		fmt.Println("Arduino Builder " + VERSION)
		fmt.Println("Copyright (C) 2015 Arduino LLC and contributors")
		fmt.Println("See https://www.arduino.cc/ and https://github.com/arduino/arduino-builder/graphs/contributors")
		fmt.Println("This is free software; see the source for copying conditions.  There is NO")
		fmt.Println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.")
		defer os.Exit(0)
		return
	}

	context := make(map[string]interface{})

	buildOptions := make(map[string]string)
	if *buildOptionsFileFlag != "" {
		if _, err := os.Stat(*buildOptionsFileFlag); err == nil {
			data, err := ioutil.ReadFile(*buildOptionsFileFlag)
			if err != nil {
				printCompleteError(err)
				defer os.Exit(1)
				return
			}
			err = json.Unmarshal(data, &buildOptions)
			if err != nil {
				printCompleteError(err)
				defer os.Exit(1)
				return
			}
		}
	}

	var err error
	printStackTrace := false
	err, printStackTrace = setContextSliceKeyOrLoadItFromOptions(context, hardwareFoldersFlag, buildOptions, constants.CTX_HARDWARE_FOLDERS, FLAG_HARDWARE, true)
	if err != nil {
		printError(err, printStackTrace)
		defer os.Exit(1)
		return
	}

	err, printStackTrace = setContextSliceKeyOrLoadItFromOptions(context, toolsFoldersFlag, buildOptions, constants.CTX_TOOLS_FOLDERS, FLAG_TOOLS, true)
	if err != nil {
		printError(err, printStackTrace)
		defer os.Exit(1)
		return
	}

	err, printStackTrace = setContextSliceKeyOrLoadItFromOptions(context, librariesFoldersFlag, buildOptions, constants.CTX_OTHER_LIBRARIES_FOLDERS, FLAG_LIBRARIES, false)
	if err != nil {
		printError(err, printStackTrace)
		defer os.Exit(1)
		return
	}

	err, printStackTrace = setContextSliceKeyOrLoadItFromOptions(context, librariesBuiltInFoldersFlag, buildOptions, constants.CTX_BUILT_IN_LIBRARIES_FOLDERS, FLAG_BUILT_IN_LIBRARIES, false)
	if err != nil {
		printError(err, printStackTrace)
		defer os.Exit(1)
		return
	}

	err, printStackTrace = setContextSliceKeyOrLoadItFromOptions(context, customBuildPropertiesFlag, buildOptions, constants.CTX_CUSTOM_BUILD_PROPERTIES, FLAG_PREFS, false)
	if err != nil {
		printError(err, printStackTrace)
		defer os.Exit(1)
		return
	}

	fqbn, err := gohasissues.Unquote(*fqbnFlag)
	if err != nil {
		printCompleteError(err)
		defer os.Exit(1)
		return
	}

	if fqbn == "" {
		fqbn = buildOptions[constants.CTX_FQBN]
	}

	if fqbn == "" {
		printErrorMessageAndFlagUsage(errors.New("Parameter '" + FLAG_FQBN + "' is mandatory"))
		defer os.Exit(1)
		return
	}
	context[constants.CTX_FQBN] = fqbn

	buildPath, err := gohasissues.Unquote(*buildPathFlag)
	if err != nil {
		printCompleteError(err)
		defer os.Exit(1)
		return
	}

	if buildPath != "" {
		_, err := os.Stat(buildPath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			defer os.Exit(1)
			return
		}

		err = utils.EnsureFolderExists(buildPath)
		if err != nil {
			printCompleteError(err)
			defer os.Exit(1)
			return
		}
	}
	context[constants.CTX_BUILD_PATH] = buildPath

	if *vidPidFlag != "" {
		context[constants.CTX_VIDPID] = *vidPidFlag
	}

	if flag.NArg() > 0 {
		sketchLocation := flag.Arg(0)
		sketchLocation, err := gohasissues.Unquote(sketchLocation)
		if err != nil {
			printCompleteError(err)
			defer os.Exit(1)
			return
		}
		context[constants.CTX_SKETCH_LOCATION] = sketchLocation
	}

	if *verboseFlag && *quietFlag {
		*verboseFlag = false
		*quietFlag = false
	}

	context[constants.CTX_VERBOSE] = *verboseFlag

	ideVersion := ""
	if utils.MapStringStringHas(buildOptions, constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION) {
		ideVersion = buildOptions[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION]
	} else {
		ideVersion = *ideVersionFlag
	}
	context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = ideVersion

	if *warningsLevelFlag != "" {
		context[constants.CTX_WARNINGS_LEVEL] = *warningsLevelFlag
	}

	if *debugLevelFlag > -1 {
		context[constants.CTX_DEBUG_LEVEL] = *debugLevelFlag
	}

	if *libraryDiscoveryRecursionDepthFlag > 0 {
		context[constants.CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH] = *libraryDiscoveryRecursionDepthFlag
	}

	if *quietFlag {
		context[constants.CTX_LOGGER] = i18n.NoopLogger{}
	} else if *loggerFlag == FLAG_LOGGER_MACHINE {
		context[constants.CTX_LOGGER] = i18n.MachineLogger{}
	} else {
		context[constants.CTX_LOGGER] = i18n.HumanLogger{}
	}

	if *dumpPrefsFlag {
		err = builder.RunParseHardwareAndDumpBuildProperties(context)
	} else if *preprocessFlag {
		err = builder.RunPreprocess(context)
	} else {
		if flag.NArg() == 0 {
			fmt.Fprintln(os.Stderr, "Last parameter must be the sketch to compile")
			flag.Usage()
			defer os.Exit(1)
			return
		}
		err = builder.RunBuilder(context)
	}

	exitCode := 0
	if err != nil {
		err = utils.WrapError(err)

		fmt.Fprintln(os.Stderr, err)

		if utils.DebugLevel(context) >= 10 {
			fmt.Fprintln(os.Stderr, err.(*errors.Error).ErrorStack())
		}

		exitCode = toExitCode(err)
	}

	defer os.Exit(exitCode)
}
func TestLoadHardwareMixingUserHardwareFolder(t *testing.T) {
	ctx := &types.Context{
		HardwareFolders: []string{"downloaded_hardware", filepath.Join("..", "hardware"), "hardware", "user_hardware"},
	}

	commands := []types.Command{
		&builder.AddAdditionalEntriesToContext{},
		&builder.HardwareLoader{},
		&builder.PlatformKeysRewriteLoader{},
		&builder.RewriteHardwareKeys{},
	}

	for _, command := range commands {
		err := command.Run(ctx)
		NoError(t, err)
	}

	packages := ctx.Hardware

	if runtime.GOOS == "windows" {
		//a package is a symlink, and windows does not support them
		require.Equal(t, 3, len(packages.Packages))
	} else {
		require.Equal(t, 4, len(packages.Packages))
	}

	require.NotNil(t, packages.Packages["arduino"])
	require.Equal(t, 2, len(packages.Packages["arduino"].Platforms))

	require.Equal(t, "uno", packages.Packages["arduino"].Platforms["avr"].Boards["uno"].BoardId)
	require.Equal(t, "uno", packages.Packages["arduino"].Platforms["avr"].Boards["uno"].Properties[constants.ID])

	require.Equal(t, "yun", packages.Packages["arduino"].Platforms["avr"].Boards["yun"].BoardId)
	require.Equal(t, "true", packages.Packages["arduino"].Platforms["avr"].Boards["yun"].Properties["upload.wait_for_upload_port"])

	require.Equal(t, "{build.usb_flags}", packages.Packages["arduino"].Platforms["avr"].Boards["robotMotor"].Properties["build.extra_flags"])

	require.Equal(t, "arduino_due_x", packages.Packages["arduino"].Platforms["sam"].Boards["arduino_due_x"].BoardId)

	avrPlatform := packages.Packages["arduino"].Platforms["avr"]
	require.Equal(t, "Arduino AVR Boards", avrPlatform.Properties[constants.PLATFORM_NAME])
	require.Equal(t, "-v", avrPlatform.Properties["tools.avrdude.bootloader.params.verbose"])
	require.Equal(t, "/my/personal/avrdude", avrPlatform.Properties["tools.avrdude.cmd.path"])

	require.Equal(t, "AVRISP mkII", avrPlatform.Programmers["avrispmkii"][constants.PROGRAMMER_NAME])

	require.Equal(t, "-w -x c++ -M -MG -MP", avrPlatform.Properties["preproc.includes.flags"])
	require.Equal(t, "-w -x c++ -E -CC", avrPlatform.Properties["preproc.macros.flags"])
	require.Equal(t, "\"{compiler.path}{compiler.cpp.cmd}\" {compiler.cpp.flags} {preproc.includes.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} \"{source_file}\"", avrPlatform.Properties[constants.RECIPE_PREPROC_INCLUDES])
	require.False(t, utils.MapStringStringHas(avrPlatform.Properties, "preproc.macros.compatibility_flags"))

	require.NotNil(t, packages.Packages["my_avr_platform"])
	myAVRPlatform := packages.Packages["my_avr_platform"]
	require.Equal(t, "hello world", myAVRPlatform.Properties["example"])
	myAVRPlatformAvrArch := myAVRPlatform.Platforms["avr"]
	require.Equal(t, "custom_yun", myAVRPlatformAvrArch.Boards["custom_yun"].BoardId)

	require.False(t, utils.MapStringStringHas(myAVRPlatformAvrArch.Properties, "preproc.includes.flags"))

	require.Equal(t, "{runtime.tools.ctags.path}", packages.Properties["tools.ctags.path"])
	require.Equal(t, "\"{cmd.path}\" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives \"{source_file}\"", packages.Properties["tools.ctags.pattern"])
	require.Equal(t, "{runtime.tools.avrdude.path}", packages.Properties["tools.avrdude.path"])
	require.Equal(t, "-w -x c++ -E -CC", packages.Properties["preproc.macros.flags"])

	if runtime.GOOS != "windows" {
		require.NotNil(t, packages.Packages["my_symlinked_avr_platform"])
		require.NotNil(t, packages.Packages["my_symlinked_avr_platform"].Platforms["avr"])
	}
}
func (s *SetupBuildProperties) Run(ctx *types.Context) error {
	packages := ctx.Hardware

	targetPlatform := ctx.TargetPlatform
	actualPlatform := ctx.ActualPlatform
	targetBoard := ctx.TargetBoard

	buildProperties := make(properties.Map)
	buildProperties.Merge(actualPlatform.Properties)
	buildProperties.Merge(targetPlatform.Properties)
	buildProperties.Merge(targetBoard.Properties)

	if ctx.BuildPath != "" {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_PATH] = ctx.BuildPath
	}
	if ctx.Sketch != nil {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_PROJECT_NAME] = filepath.Base(ctx.Sketch.MainFile.Name)
	}
	buildProperties[constants.BUILD_PROPERTIES_BUILD_ARCH] = strings.ToUpper(targetPlatform.PlatformId)

	buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE] = ctx.BuildCore
	buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE_PATH] = filepath.Join(actualPlatform.Folder, constants.FOLDER_CORES, buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE])
	buildProperties[constants.BUILD_PROPERTIES_BUILD_SYSTEM_PATH] = filepath.Join(actualPlatform.Folder, constants.FOLDER_SYSTEM)
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH] = targetPlatform.Folder
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_HARDWARE_PATH] = filepath.Join(targetPlatform.Folder, "..")
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = ctx.ArduinoAPIVersion
	buildProperties[constants.IDE_VERSION] = ctx.ArduinoAPIVersion
	buildProperties[constants.BUILD_PROPERTIES_RUNTIME_OS] = utils.PrettyOSName()

	variant := buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT]
	if variant == constants.EMPTY_STRING {
		buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT_PATH] = constants.EMPTY_STRING
	} else {
		var variantPlatform *types.Platform
		variantParts := strings.Split(variant, ":")
		if len(variantParts) > 1 {
			variantPlatform = packages.Packages[variantParts[0]].Platforms[targetPlatform.PlatformId]
			variant = variantParts[1]
		} else {
			variantPlatform = targetPlatform
		}
		buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT_PATH] = filepath.Join(variantPlatform.Folder, constants.FOLDER_VARIANTS, variant)
	}

	tools := ctx.Tools
	for _, tool := range tools {
		buildProperties[constants.BUILD_PROPERTIES_RUNTIME_TOOLS_PREFIX+tool.Name+constants.BUILD_PROPERTIES_RUNTIME_TOOLS_SUFFIX] = tool.Folder
		buildProperties[constants.BUILD_PROPERTIES_RUNTIME_TOOLS_PREFIX+tool.Name+"-"+tool.Version+constants.BUILD_PROPERTIES_RUNTIME_TOOLS_SUFFIX] = tool.Folder
	}

	if !utils.MapStringStringHas(buildProperties, constants.BUILD_PROPERTIES_SOFTWARE) {
		buildProperties[constants.BUILD_PROPERTIES_SOFTWARE] = DEFAULT_SOFTWARE
	}

	if ctx.SketchLocation != "" {
		sourcePath, err := filepath.Abs(ctx.SketchLocation)
		if err != nil {
			return err
		}
		sourcePath = filepath.Dir(sourcePath)
		buildProperties[constants.BUILD_PROPERTIES_SOURCE_PATH] = sourcePath
	}

	now := time.Now()
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_UTC] = strconv.FormatInt(now.Unix(), 10)
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_LOCAL] = strconv.FormatInt(timeutils.LocalUnix(now), 10)
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_ZONE] = strconv.Itoa(timeutils.TimezoneOffsetNoDST(now))
	buildProperties[constants.BUILD_PROPERTIES_EXTRA_TIME_DST] = strconv.Itoa(timeutils.DaylightSavingsOffset(now))

	ctx.BuildProperties = buildProperties

	return nil
}