func (s *RecipeByPrefixSuffixRunner) Run(context map[string]interface{}) error { logger := context[constants.CTX_LOGGER].(i18n.Logger) if utils.DebugLevel(context) >= 10 { logger.Fprintln(os.Stderr, constants.MSG_LOOKING_FOR_RECIPES, s.Prefix, s.Suffix) } buildProperties := utils.GetMapStringStringOrDefault(context, constants.CTX_BUILD_PROPERTIES) verbose := context[constants.CTX_VERBOSE].(bool) recipes := findRecipes(buildProperties, s.Prefix, s.Suffix) properties := utils.MergeMapsOfStrings(make(map[string]string), buildProperties) for _, recipe := range recipes { if utils.DebugLevel(context) >= 10 { logger.Fprintln(os.Stderr, constants.MSG_RUNNING_RECIPE, recipe) } _, err := builder_utils.ExecRecipe(properties, recipe, false, verbose, verbose, logger) if err != nil { return utils.WrapError(err) } } return nil }
func (s *RecipeByPrefixSuffixRunner) Run(context map[string]interface{}) error { logger := context[constants.CTX_LOGGER].(i18n.Logger) if utils.DebugLevel(context) >= 10 { logger.Fprintln(os.Stdout, constants.LOG_LEVEL_DEBUG, constants.MSG_LOOKING_FOR_RECIPES, s.Prefix, s.Suffix) } buildProperties := make(props.PropertiesMap) if p, ok := context[constants.CTX_BUILD_PROPERTIES]; ok { buildProperties = p.(props.PropertiesMap).Clone() } verbose := context[constants.CTX_VERBOSE].(bool) recipes := findRecipes(buildProperties, s.Prefix, s.Suffix) properties := buildProperties.Clone() for _, recipe := range recipes { if utils.DebugLevel(context) >= 10 { logger.Fprintln(os.Stdout, constants.LOG_LEVEL_DEBUG, constants.MSG_RUNNING_RECIPE, recipe) } _, err := builder_utils.ExecRecipe(properties, recipe, false, verbose, verbose, logger) if err != nil { return utils.WrapError(err) } } return nil }
func (s *RewriteHardwareKeys) Run(context map[string]interface{}) error { if !utils.MapHas(context, constants.CTX_PLATFORM_KEYS_REWRITE) { return nil } hardware := context[constants.CTX_HARDWARE].(map[string]*types.Package) platformKeysRewrite := context[constants.CTX_PLATFORM_KEYS_REWRITE].(types.PlatforKeysRewrite) logger := context[constants.CTX_LOGGER].(i18n.Logger) warn := utils.DebugLevel(context) > 0 for _, aPackage := range hardware { for _, platform := range aPackage.Platforms { if platform.Properties[constants.REWRITING] != constants.REWRITING_DISABLED { for _, rewrite := range platformKeysRewrite.Rewrites { if platform.Properties[rewrite.Key] != constants.EMPTY_STRING && platform.Properties[rewrite.Key] == rewrite.OldValue { platform.Properties[rewrite.Key] = rewrite.NewValue if warn { logger.Fprintln(os.Stderr, constants.MSG_WARNING_PLATFORM_OLD_VALUES, platform.Properties[constants.PLATFORM_NAME], rewrite.Key+"="+rewrite.OldValue, rewrite.Key+"="+rewrite.NewValue) } } } } } } return nil }
func (s *IncludesToIncludeFolders) Run(context map[string]interface{}) error { if !utils.MapHas(context, constants.CTX_LIBRARIES) { return nil } includes := context[constants.CTX_INCLUDES].([]string) headerToLibraries := context[constants.CTX_HEADER_TO_LIBRARIES].(map[string][]*types.Library) debugLevel := utils.DebugLevel(context) logger := context[constants.CTX_LOGGER].(i18n.Logger) platform := context[constants.CTX_TARGET_PLATFORM].(*types.Platform) actualPlatform := context[constants.CTX_ACTUAL_PLATFORM].(*types.Platform) var previousImportedLibraries []*types.Library if utils.MapHas(context, constants.CTX_IMPORTED_LIBRARIES) { previousImportedLibraries = context[constants.CTX_IMPORTED_LIBRARIES].([]*types.Library) } importedLibraries, err := resolveLibraries(includes, headerToLibraries, previousImportedLibraries, []*types.Platform{actualPlatform, platform}, debugLevel, logger) if err != nil { return utils.WrapError(err) } context[constants.CTX_IMPORTED_LIBRARIES] = importedLibraries buildProperties := context[constants.CTX_BUILD_PROPERTIES].(map[string]string) verbose := context[constants.CTX_VERBOSE].(bool) includeFolders := resolveIncludeFolders(importedLibraries, buildProperties, verbose) context[constants.CTX_INCLUDE_FOLDERS] = includeFolders return nil }
func (s *WarnAboutArchIncompatibleLibraries) Run(context map[string]interface{}) error { if utils.DebugLevel(context) <= 0 { return nil } targetPlatform := context[constants.CTX_TARGET_PLATFORM].(*types.Platform) buildProperties := context[constants.CTX_BUILD_PROPERTIES].(props.PropertiesMap) logger := context[constants.CTX_LOGGER].(i18n.Logger) archs := []string{} archs = append(archs, targetPlatform.PlatformId) if buildProperties[constants.BUILD_PROPERTIES_ARCH_OVERRIDE_CHECK] != constants.EMPTY_STRING { overrides := strings.Split(buildProperties[constants.BUILD_PROPERTIES_ARCH_OVERRIDE_CHECK], ",") for _, override := range overrides { archs = append(archs, override) } } importedLibraries := context[constants.CTX_IMPORTED_LIBRARIES].([]*types.Library) for _, importedLibrary := range importedLibraries { if !importedLibrary.SupportsArchitectures(archs) { logger.Fprintln(os.Stdout, constants.LOG_LEVEL_WARN, constants.MSG_LIBRARY_INCOMPATIBLE_ARCH, importedLibrary.Name, importedLibrary.Archs, archs) } } return nil }
func (s *WarnAboutPlatformRewrites) Run(context map[string]interface{}) error { warn := utils.DebugLevel(context) > 0 if !warn { return nil } logger := context[constants.CTX_LOGGER].(i18n.Logger) hardwareRewriteResults := context[constants.CTX_HARDWARE_REWRITE_RESULTS].(map[*types.Platform][]types.PlatforKeyRewrite) targetPlatform := context[constants.CTX_TARGET_PLATFORM].(*types.Platform) actualPlatform := context[constants.CTX_ACTUAL_PLATFORM].(*types.Platform) platforms := []*types.Platform{targetPlatform} if actualPlatform != targetPlatform { platforms = append(platforms, actualPlatform) } for _, platform := range platforms { if hardwareRewriteResults[platform] != nil { for _, rewrite := range hardwareRewriteResults[platform] { logger.Fprintln(os.Stdout, constants.LOG_LEVEL_WARN, constants.MSG_WARNING_PLATFORM_OLD_VALUES, platform.Properties[constants.PLATFORM_NAME], rewrite.Key+"="+rewrite.OldValue, rewrite.Key+"="+rewrite.NewValue) } } } return nil }
func skipTagsWithField(tags []map[string]string, fields []string, context map[string]interface{}) { for _, tag := range tags { if field, skip := utils.TagHasAtLeastOneField(tag, fields); skip { if utils.DebugLevel(context) >= 10 { utils.Logger(context).Fprintln(os.Stderr, constants.MSG_SKIPPING_TAG_BECAUSE_HAS_FIELD, field) } tag[FIELD_SKIP] = TRUE } } }
func skipTagsWhere(tags []map[string]string, skipFunc skipFuncType, context map[string]interface{}) { for _, tag := range tags { if tag[FIELD_SKIP] != TRUE { skip := skipFunc(tag) if skip && utils.DebugLevel(context) >= 10 { utils.Logger(context).Fprintln(os.Stderr, constants.MSG_SKIPPING_TAG_WITH_REASON, tag[FIELD_FUNCTION_NAME], runtime.FuncForPC(reflect.ValueOf(skipFunc).Pointer()).Name()) } tag[FIELD_SKIP] = strconv.FormatBool(skip) } } }
func skipTagsWhere(tags []*types.CTag, skipFunc skipFuncType, context map[string]interface{}) { for _, tag := range tags { if !tag.SkipMe { skip := skipFunc(tag) if skip && utils.DebugLevel(context) >= 10 { utils.Logger(context).Fprintln(os.Stdout, constants.LOG_LEVEL_DEBUG, constants.MSG_SKIPPING_TAG_WITH_REASON, tag.FunctionName, runtime.FuncForPC(reflect.ValueOf(skipFunc).Pointer()).Name()) } tag.SkipMe = skip } } }
func removeDefinedProtypes(tags []map[string]string, context map[string]interface{}) { definedPrototypes := make(map[string]bool) for _, tag := range tags { if tag[FIELD_KIND] == KIND_PROTOTYPE { definedPrototypes[tag[KIND_PROTOTYPE]] = true } } for _, tag := range tags { if definedPrototypes[tag[KIND_PROTOTYPE]] { if utils.DebugLevel(context) >= 10 { utils.Logger(context).Fprintln(os.Stderr, constants.MSG_SKIPPING_TAG_ALREADY_DEFINED, tag[FIELD_FUNCTION_NAME]) } tag[FIELD_SKIP] = TRUE } } }
func removeDefinedProtypes(tags []*types.CTag, context map[string]interface{}) { definedPrototypes := make(map[string]bool) for _, tag := range tags { if tag.Kind == KIND_PROTOTYPE { definedPrototypes[tag.Prototype] = true } } for _, tag := range tags { if definedPrototypes[tag.Prototype] { if utils.DebugLevel(context) >= 10 { utils.Logger(context).Fprintln(os.Stdout, constants.LOG_LEVEL_DEBUG, constants.MSG_SKIPPING_TAG_ALREADY_DEFINED, tag.FunctionName) } tag.SkipMe = true } } }
func (s *PrintUsedAndNotUsedLibraries) Run(context map[string]interface{}) error { if utils.DebugLevel(context) <= 0 { return nil } logger := context[constants.CTX_LOGGER].(i18n.Logger) libraryResolutionResults := context[constants.CTX_LIBRARY_RESOLUTION_RESULTS].(map[string]types.LibraryResolutionResult) for header, libResResult := range libraryResolutionResults { if !libResResult.IsLibraryFromPlatform { logger.Fprintln(os.Stderr, constants.MSG_LIBRARIES_MULTIPLE_LIBS_FOUND_FOR, header) logger.Fprintln(os.Stderr, constants.MSG_LIBRARIES_USED, libResResult.Library.Folder) for _, notUsedLibrary := range libResResult.NotUsedLibraries { logger.Fprintln(os.Stderr, constants.MSG_LIBRARIES_NOT_USED, notUsedLibrary.Folder) } } } time.Sleep(100 * time.Millisecond) return nil }
func (s *GenerateBuildPathIfMissing) Run(context map[string]interface{}) error { if utils.MapHas(context, constants.CTX_BUILD_PATH) && context[constants.CTX_BUILD_PATH].(string) != constants.EMPTY_STRING { return nil } sketchLocation := context[constants.CTX_SKETCH_LOCATION].(string) md5sum := utils.MD5Sum([]byte(sketchLocation)) buildPath := filepath.Join(os.TempDir(), "arduino-sketch-"+strings.ToUpper(md5sum)) _, err := os.Stat(buildPath) if err != nil && !os.IsNotExist(err) { return utils.WrapError(err) } if utils.DebugLevel(context) > 5 { logger := context[constants.CTX_LOGGER].(i18n.Logger) logger.Fprintln(os.Stdout, constants.LOG_LEVEL_WARN, constants.MSG_SETTING_BUILD_PATH, buildPath) } context[constants.CTX_BUILD_PATH] = buildPath return nil }
func (s *LibrariesLoader) Run(context map[string]interface{}) error { sortedLibrariesFolders := []string{} builtInLibrariesFolders := []string{} if utils.MapHas(context, constants.CTX_BUILT_IN_LIBRARIES_FOLDERS) { builtInLibrariesFolders = context[constants.CTX_BUILT_IN_LIBRARIES_FOLDERS].([]string) } builtInLibrariesFolders, err := utils.AbsolutizePaths(builtInLibrariesFolders) if err != nil { return utils.WrapError(err) } sortedLibrariesFolders = utils.AppendIfNotPresent(sortedLibrariesFolders, builtInLibrariesFolders...) platform := context[constants.CTX_TARGET_PLATFORM].(*types.Platform) debugLevel := utils.DebugLevel(context) logger := context[constants.CTX_LOGGER].(i18n.Logger) actualPlatform := context[constants.CTX_ACTUAL_PLATFORM].(*types.Platform) if actualPlatform != platform { sortedLibrariesFolders = appendPathToLibrariesFolders(sortedLibrariesFolders, filepath.Join(actualPlatform.Folder, constants.FOLDER_LIBRARIES)) } sortedLibrariesFolders = appendPathToLibrariesFolders(sortedLibrariesFolders, filepath.Join(platform.Folder, constants.FOLDER_LIBRARIES)) librariesFolders := []string{} if utils.MapHas(context, constants.CTX_OTHER_LIBRARIES_FOLDERS) { librariesFolders = context[constants.CTX_OTHER_LIBRARIES_FOLDERS].([]string) } librariesFolders, err = utils.AbsolutizePaths(librariesFolders) if err != nil { return utils.WrapError(err) } sortedLibrariesFolders = utils.AppendIfNotPresent(sortedLibrariesFolders, librariesFolders...) context[constants.CTX_LIBRARIES_FOLDERS] = sortedLibrariesFolders var libraries []*types.Library for _, libraryFolder := range sortedLibrariesFolders { subFolders, err := utils.ReadDirFiltered(libraryFolder, utils.FilterDirs) if err != nil { return utils.WrapError(err) } for _, subFolder := range subFolders { library, err := makeLibrary(filepath.Join(libraryFolder, subFolder.Name()), debugLevel, logger) if err != nil { return utils.WrapError(err) } libraries = append(libraries, library) } } context[constants.CTX_LIBRARIES] = libraries headerToLibraries := make(map[string][]*types.Library) for _, library := range libraries { headers, err := utils.ReadDirFiltered(library.SrcFolder, utils.FilterFilesWithExtension(".h")) if err != nil { return utils.WrapError(err) } for _, header := range headers { headerFileName := header.Name() headerToLibraries[headerFileName] = append(headerToLibraries[headerFileName], library) } } context[constants.CTX_HEADER_TO_LIBRARIES] = headerToLibraries return nil }
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 PrintRingNameIfDebug(context map[string]interface{}, command types.Command) { if utils.DebugLevel(context) >= 10 { utils.Logger(context).Fprintln(os.Stderr, constants.MSG_RUNNING_COMMAND, reflect.Indirect(reflect.ValueOf(command)).Type().Name()) } }
func PrintRingNameIfDebug(context map[string]interface{}, command types.Command) { if utils.DebugLevel(context) >= 10 { utils.Logger(context).Fprintln(os.Stdout, constants.LOG_LEVEL_DEBUG, constants.MSG_RUNNING_COMMAND, strconv.FormatInt(time.Now().Unix(), 10), reflect.Indirect(reflect.ValueOf(command)).Type().Name()) } }
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 } compile := *compileFlag dumpPrefs := *dumpPrefsFlag if compile && dumpPrefs { fmt.Println("You can either specify --compile or --dump-prefs, not both") defer os.Exit(1) return } if !compile && !dumpPrefs { compile = true } context := make(map[string]interface{}) hardware, err := toSliceOfUnquoted(hardwareFoldersFlag) if err != nil { printCompleteError(err) defer os.Exit(1) return } if len(hardware) == 0 { fmt.Println("Parameter 'hardware' is mandatory") flag.Usage() defer os.Exit(1) return } context[constants.CTX_HARDWARE_FOLDERS] = hardware tools, err := toSliceOfUnquoted(toolsFoldersFlag) if err != nil { printCompleteError(err) defer os.Exit(1) return } if len(tools) == 0 { fmt.Println("Parameter 'tools' is mandatory") flag.Usage() defer os.Exit(1) return } context[constants.CTX_TOOLS_FOLDERS] = tools libraries, err := toSliceOfUnquoted(librariesFoldersFlag) if err != nil { printCompleteError(err) defer os.Exit(1) return } context[constants.CTX_LIBRARIES_FOLDERS] = libraries customBuildProperties, err := toSliceOfUnquoted(customBuildPropertiesFlag) if err != nil { printCompleteError(err) defer os.Exit(1) return } context[constants.CTX_CUSTOM_BUILD_PROPERTIES] = customBuildProperties fqbn, err := gohasissues.Unquote(*fqbnFlag) if err != nil { printCompleteError(err) defer os.Exit(1) return } if fqbn == "" { fmt.Println("Parameter 'fqbn' is mandatory") flag.Usage() 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.Println(err) defer os.Exit(1) return } err = os.MkdirAll(buildPath, os.FileMode(0755)) if err != nil { printCompleteError(err) defer os.Exit(1) return } } context[constants.CTX_BUILD_PATH] = buildPath if compile && flag.NArg() == 0 { fmt.Println("Last parameter must be the sketch to compile") flag.Usage() defer os.Exit(1) return } 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 } context[constants.CTX_VERBOSE] = *verboseFlag context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = *ideVersionFlag 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 *loggerFlag == "machine" { context[constants.CTX_LOGGER] = i18n.MachineLogger{} } else { context[constants.CTX_LOGGER] = i18n.HumanLogger{} } if compile { err = builder.RunBuilder(context) } else if dumpPrefs { err = builder.RunParseHardwareAndDumpBuildProperties(context) } exitCode := 0 if err != nil { err = utils.WrapError(err) fmt.Println(err) if utils.DebugLevel(context) >= 10 { fmt.Println(err.(*errors.Error).ErrorStack()) } exitCode = toExitCode(err) } defer os.Exit(exitCode) }