func (s *AdditionalSketchFilesCopier) Run(context map[string]interface{}) error {
	sketch := context[constants.CTX_SKETCH].(*types.Sketch)
	sketchBuildPath := context[constants.CTX_SKETCH_BUILD_PATH].(string)

	err := utils.EnsureFolderExists(sketchBuildPath)
	if err != nil {
		return utils.WrapError(err)
	}

	sketchBasePath := filepath.Dir(sketch.MainFile.Name)

	for _, file := range sketch.AdditionalFiles {
		relativePath, err := filepath.Rel(sketchBasePath, file.Name)
		if err != nil {
			return utils.WrapError(err)
		}

		targetFilePath := filepath.Join(sketchBuildPath, relativePath)
		err = utils.EnsureFolderExists(filepath.Dir(targetFilePath))
		if err != nil {
			return utils.WrapError(err)
		}

		bytes, err := ioutil.ReadFile(file.Name)
		if err != nil {
			return utils.WrapError(err)
		}

		utils.WriteFileBytes(targetFilePath, bytes)
	}

	return nil
}
func patchFiles(t *testing.T) {
	files, err := ioutil.ReadDir(PATCHES_FOLDER)
	NoError(t, err)

	for _, file := range files {
		if filepath.Ext(file.Name()) == ".patch" {
			data, err := ioutil.ReadFile(Abs(t, filepath.Join(PATCHES_FOLDER, file.Name())))
			NoError(t, err)
			patchSet, err := patch.Parse(data)
			NoError(t, err)
			operations, err := patchSet.Apply(ioutil.ReadFile)
			for _, op := range operations {
				utils.WriteFileBytes(op.Dst, op.Data)
			}
		}
	}
}
func downloadAndUnpack(url string) (string, []os.FileInfo, error) {
	fmt.Fprintln(os.Stderr, "Downloading "+url)

	unpackFolder, err := ioutil.TempDir(constants.EMPTY_STRING, "arduino-builder-tool")
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}

	urlParts := strings.Split(url, "/")
	archiveFileName := urlParts[len(urlParts)-1]
	archiveFilePath := filepath.Join(unpackFolder, archiveFileName)

	res, err := http.Get(url)
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}

	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}
	res.Body.Close()

	utils.WriteFileBytes(archiveFilePath, bytes)

	cmd := buildUnpackCmd(archiveFilePath)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}
	if len(out) > 0 {
		fmt.Println(string(out))
	}

	os.Remove(archiveFilePath)

	files, err := gohasissues.ReadDir(unpackFolder)
	if err != nil {
		return constants.EMPTY_STRING, nil, i18n.WrapError(err)
	}

	return unpackFolder, files, nil
}
// Write the given cache to the given file if it is invalidated. If the
// cache is still valid, just update the timestamps of the file.
func writeCache(cache *includeCache, path string) error {
	// If the cache was still valid all the way, just touch its file
	// (in case any source file changed without influencing the
	// includes). If it was invalidated, overwrite the cache with
	// the new contents.
	if cache.valid {
		os.Chtimes(path, time.Now(), time.Now())
	} else {
		bytes, err := json.MarshalIndent(cache.entries, "", "  ")
		if err != nil {
			return i18n.WrapError(err)
		}
		err = utils.WriteFileBytes(path, bytes)
		if err != nil {
			return i18n.WrapError(err)
		}
	}
	return nil
}
func (s *AdditionalSketchFilesCopier) Run(ctx *types.Context) error {
	sketch := ctx.Sketch
	sketchBuildPath := ctx.SketchBuildPath

	err := utils.EnsureFolderExists(sketchBuildPath)
	if err != nil {
		return i18n.WrapError(err)
	}

	sketchBasePath := filepath.Dir(sketch.MainFile.Name)

	for _, file := range sketch.AdditionalFiles {
		relativePath, err := filepath.Rel(sketchBasePath, file.Name)
		if err != nil {
			return i18n.WrapError(err)
		}

		targetFilePath := filepath.Join(sketchBuildPath, relativePath)
		err = utils.EnsureFolderExists(filepath.Dir(targetFilePath))
		if err != nil {
			return i18n.WrapError(err)
		}

		bytes, err := ioutil.ReadFile(file.Name)
		if err != nil {
			return i18n.WrapError(err)
		}

		if targetFileChanged(bytes, targetFilePath) {
			err := utils.WriteFileBytes(targetFilePath, bytes)
			if err != nil {
				return i18n.WrapError(err)
			}
		}
	}

	return nil
}