diff --git a/docs/sketch-build-process.md b/docs/sketch-build-process.md index 25347481a23..2bbe95cd61c 100644 --- a/docs/sketch-build-process.md +++ b/docs/sketch-build-process.md @@ -27,6 +27,9 @@ compiler: rare cases, prototype generation may fail for some functions. To work around this, you can provide your own prototypes for these functions. - `#line` directives are added to make warning or error messages reflect the original sketch layout. +- Special and optional sketch variables named ARDUIFINE and ARDUINOGLOBAL are parsed, and their content is used to + provide more arguments to the compiler commands. Those arguments are used for every compiled source file, including + the core, internal and external libraries. Their content is displayed in the message area. No pre-processing is done to files in a sketch with any extension other than .ino or .pde. Additionally, .h files in the sketch are not automatically #included from the main sketch file. Further, if you want to call functions defined in a .c diff --git a/i18n/data/en.po b/i18n/data/en.po index 088e6d19e41..88c201da28a 100644 --- a/i18n/data/en.po +++ b/i18n/data/en.po @@ -61,7 +61,7 @@ msgstr "%s is not managed by package manager" msgid "%s must be installed." msgstr "%s must be installed." -#: legacy/builder/builder_utils/utils.go:530 +#: legacy/builder/builder_utils/utils.go:533 #: legacy/builder/ctags_runner.go:41 msgid "%s pattern is missing" msgstr "%s pattern is missing" @@ -110,6 +110,10 @@ msgstr "ARCH" msgid "ARDUINO COMMAND LINE MANUAL" msgstr "ARDUINO COMMAND LINE MANUAL" +#: legacy/builder/phases/libraries_builder.go:48 +msgid "Additional compiler options provided by user sketch: {0}" +msgstr "Additional compiler options provided by user sketch: {0}" + #: cli/usage.go:32 msgid "Additional help topics:" msgstr "Additional help topics:" @@ -349,7 +353,7 @@ msgstr "Check dependencies status for the specified library." msgid "Checking lib install prerequisites" msgstr "Checking lib install prerequisites" -#: legacy/builder/builder_utils/utils.go:280 +#: legacy/builder/builder_utils/utils.go:283 msgid "Checking previous results for {0} (result = {1}, dep = {2})" msgstr "Checking previous results for {0} (result = {1}, dep = {2})" @@ -391,7 +395,7 @@ msgstr "Compiling core..." msgid "Compiling libraries..." msgstr "Compiling libraries..." -#: legacy/builder/phases/libraries_builder.go:135 +#: legacy/builder/phases/libraries_builder.go:139 msgid "Compiling library \"{0}\"" msgstr "Compiling library \"{0}\"" @@ -525,7 +529,7 @@ msgstr "Deletes a settings key and all its sub keys." msgid "Dependencies: %s" msgstr "Dependencies: %s" -#: legacy/builder/builder_utils/utils.go:358 +#: legacy/builder/builder_utils/utils.go:361 msgid "Depfile is about different file: {0}" msgstr "Depfile is about different file: {0}" @@ -794,7 +798,7 @@ msgstr "Error getting libraries info: %v" msgid "Error getting port settings details: %s" msgstr "Error getting port settings details: %s" -#: legacy/builder/types/context.go:239 +#: legacy/builder/types/context.go:244 msgid "Error in FQBN: %s" msgstr "Error in FQBN: %s" @@ -1059,7 +1063,7 @@ msgstr "Failed to listen on TCP port: %[1]s. Unexpected error: %[2]v" msgid "Failed to listen on TCP port: %s. Address already in use." msgstr "Failed to listen on TCP port: %s. Address already in use." -#: legacy/builder/builder_utils/utils.go:380 +#: legacy/builder/builder_utils/utils.go:383 msgid "Failed to read: {0}" msgstr "Failed to read: {0}" @@ -1358,7 +1362,7 @@ msgstr "Library installed" msgid "Library name" msgstr "Library name" -#: legacy/builder/phases/libraries_builder.go:91 +#: legacy/builder/phases/libraries_builder.go:95 msgid "Library {0} has been declared precompiled:" msgstr "Library {0} has been declared precompiled:" @@ -1511,7 +1515,7 @@ msgstr "New version" msgid "No boards found." msgstr "No boards found." -#: legacy/builder/builder_utils/utils.go:351 +#: legacy/builder/builder_utils/utils.go:354 msgid "No colon in first line of depfile" msgstr "No colon in first line of depfile" @@ -1563,13 +1567,13 @@ msgstr "No valid dependencies solution found" msgid "Not enough memory; see %s for tips on reducing your footprint." msgstr "Not enough memory; see %s for tips on reducing your footprint." -#: legacy/builder/builder_utils/utils.go:284 +#: legacy/builder/builder_utils/utils.go:287 msgid "Not found: nil" msgstr "Not found: nil" -#: legacy/builder/builder_utils/utils.go:300 -#: legacy/builder/builder_utils/utils.go:313 -#: legacy/builder/builder_utils/utils.go:387 +#: legacy/builder/builder_utils/utils.go:303 +#: legacy/builder/builder_utils/utils.go:316 +#: legacy/builder/builder_utils/utils.go:390 msgid "Not found: {0}" msgstr "Not found: {0}" @@ -1745,8 +1749,8 @@ msgstr "Port closed:" msgid "Port monitor error" msgstr "Port monitor error" -#: legacy/builder/phases/libraries_builder.go:101 -#: legacy/builder/phases/libraries_builder.go:109 +#: legacy/builder/phases/libraries_builder.go:105 +#: legacy/builder/phases/libraries_builder.go:113 msgid "Precompiled library in \"{0}\" not found" msgstr "Precompiled library in \"{0}\" not found" @@ -2004,11 +2008,11 @@ msgstr "Skip linking of final executable." msgid "Skipping 1200-bps touch reset: no serial port selected!" msgstr "Skipping 1200-bps touch reset: no serial port selected!" -#: legacy/builder/builder_utils/utils.go:476 +#: legacy/builder/builder_utils/utils.go:479 msgid "Skipping archive creation of: {0}" msgstr "Skipping archive creation of: {0}" -#: legacy/builder/builder_utils/utils.go:269 +#: legacy/builder/builder_utils/utils.go:272 msgid "Skipping compile of: {0}" msgstr "Skipping compile of: {0}" @@ -2069,7 +2073,7 @@ msgstr "The key '%[1]v' is not a list of items, can't remove from it.\n" msgid "The output format for the logs, can be: %s" msgstr "The output format for the logs, can be: %s" -#: legacy/builder/phases/libraries_builder.go:151 +#: legacy/builder/phases/libraries_builder.go:155 msgid "The platform does not support '{0}' for precompiled libraries." msgstr "The platform does not support '{0}' for precompiled libraries." @@ -2333,13 +2337,13 @@ msgstr "Using library {0} in folder: {1} {2}" msgid "Using precompiled core: {0}" msgstr "Using precompiled core: {0}" -#: legacy/builder/phases/libraries_builder.go:98 -#: legacy/builder/phases/libraries_builder.go:106 +#: legacy/builder/phases/libraries_builder.go:102 +#: legacy/builder/phases/libraries_builder.go:110 msgid "Using precompiled library in {0}" msgstr "Using precompiled library in {0}" -#: legacy/builder/builder_utils/utils.go:267 -#: legacy/builder/builder_utils/utils.go:499 +#: legacy/builder/builder_utils/utils.go:270 +#: legacy/builder/builder_utils/utils.go:502 msgid "Using previously compiled file: {0}" msgstr "Using previously compiled file: {0}" @@ -3429,9 +3433,9 @@ msgstr "wrong format in server response" msgid "{0} invalid, rebuilding all" msgstr "{0} invalid, rebuilding all" -#: legacy/builder/builder_utils/utils.go:323 -#: legacy/builder/builder_utils/utils.go:329 -#: legacy/builder/builder_utils/utils.go:393 +#: legacy/builder/builder_utils/utils.go:326 +#: legacy/builder/builder_utils/utils.go:332 +#: legacy/builder/builder_utils/utils.go:396 msgid "{0} newer than {1}" msgstr "{0} newer than {1}" diff --git a/legacy/builder/builder_utils/utils.go b/legacy/builder/builder_utils/utils.go index 5a504aa29a2..67b6992787d 100644 --- a/legacy/builder/builder_utils/utils.go +++ b/legacy/builder/builder_utils/utils.go @@ -232,6 +232,9 @@ func compileFileWithRecipe(ctx *types.Context, sourcePath *paths.Path, source *p properties := buildProperties.Clone() properties.Set(constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS, properties.Get(constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS+"."+ctx.WarningsLevel)) properties.Set(constants.BUILD_PROPERTIES_INCLUDES, strings.Join(includes, constants.SPACE)) + if len(ctx.Arduifines) > 0 { + properties.Set(constants.BUILD_PROPERTIES_INCLUDES, properties.Get(constants.BUILD_PROPERTIES_INCLUDES) + " " + ctx.Arduifines + " ") + } properties.SetPath(constants.BUILD_PROPERTIES_SOURCE_FILE, source) relativeSource, err := sourcePath.RelTo(source) if err != nil { diff --git a/legacy/builder/ctags/ctags_parser.go b/legacy/builder/ctags/ctags_parser.go index 97a2a2a5ee1..1953afe621a 100644 --- a/legacy/builder/ctags/ctags_parser.go +++ b/legacy/builder/ctags/ctags_parser.go @@ -32,6 +32,8 @@ const KIND_FUNCTION = "function" const TEMPLATE = "template" const STATIC = "static" const EXTERN = "extern \"C\"" +const ARDUIFINE = "ARDUIFINE" +const ARDUINOGLOBAL = "ARDUINOGLOBAL" var KNOWN_TAG_KINDS = map[string]bool{ "prototype": true, @@ -43,14 +45,21 @@ type CTagsParser struct { mainFile *paths.Path } -func (p *CTagsParser) Parse(ctagsOutput string, mainFile *paths.Path) []*types.CTag { +func (p *CTagsParser) Parse(ctagsOutput string, mainFile *paths.Path) ([]*types.CTag, string) { + Arduifines := "" rows := strings.Split(ctagsOutput, "\n") rows = removeEmpty(rows) p.mainFile = mainFile for _, row := range rows { - p.tags = append(p.tags, parseTag(row)) + if strings.Index(row, " "+ARDUINOGLOBAL) != -1 { + Arduifines += encloseForCmdLine(extractCtagsDoubleQuotedString(row, ARDUINOGLOBAL)) + } else if strings.Index(row, " "+ARDUIFINE) != -1 { + Arduifines += encloseForCmdLine(toCompilerCmdLine(extractCtagsDoubleQuotedString(row, ARDUIFINE))) + } else { + p.tags = append(p.tags, parseTag(row)) + } } p.skipTagsWhere(tagIsUnknown) @@ -60,7 +69,7 @@ func (p *CTagsParser) Parse(ctagsOutput string, mainFile *paths.Path) []*types.C p.skipDuplicates() p.skipTagsWhere(p.prototypeAndCodeDontMatch) - return p.tags + return p.tags, Arduifines } func (p *CTagsParser) addPrototypes() { @@ -238,3 +247,36 @@ func removeEmpty(rows []string) []string { return newRows } + +func extractCtagsDoubleQuotedString (row string, directive string) string { + first := strings.Index(row, "\""); + last := strings.LastIndex(row, "\";$/;\""); // <- $/;" is a ctag addition + if (first <= 0) || (last <= 0) || (first + 1 >= last) { + //print("\nERROR: malformed \"" + directive + "\" global directive\n\n") + return "" + } + return strings.Replace(strings.Replace(row[first+1 : last], "\\\\", "\\", -1), "\\\"", "\"", -1) +} + +func toCompilerCmdLine (define string) string { + // transforms ` A = "B C" ` to `-DA="B C"` + // transforms ` A ` to `-DA` + elts := strings.SplitAfterN(define, "=", 2) + ret := "" + eltsLength := len(elts) + if eltsLength > 0 { + equalsLength := eltsLength - 1 + ret += "-D"+strings.TrimSpace(elts[0][:len(elts[0])-equalsLength]) + if equalsLength > 0 { + ret += "="+strings.TrimSpace(elts[1]) + } + } + return ret +} + +func encloseForCmdLine (str string) string { + if len(str) > 0 { + return " \""+str+"\" " + } + return "" +} diff --git a/legacy/builder/ctags/ctags_parser_test.go b/legacy/builder/ctags/ctags_parser_test.go index 13e10e8e79c..25de6998483 100644 --- a/legacy/builder/ctags/ctags_parser_test.go +++ b/legacy/builder/ctags/ctags_parser_test.go @@ -30,7 +30,9 @@ func produceTags(t *testing.T, filename string) []*types.CTag { require.NoError(t, err) parser := CTagsParser{} - return parser.Parse(string(bytes), nil) + tags, Arduifines := parser.Parse(string(bytes), nil) + _ = Arduifines + return tags } func TestCTagsParserShouldListPrototypes(t *testing.T) { diff --git a/legacy/builder/ctags_runner.go b/legacy/builder/ctags_runner.go index 299ffb45962..f6df64bdea3 100644 --- a/legacy/builder/ctags_runner.go +++ b/legacy/builder/ctags_runner.go @@ -56,7 +56,7 @@ func (s *CTagsRunner) Run(ctx *types.Context) error { parser := &ctags.CTagsParser{} - ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput, ctx.Sketch.MainFile) + ctx.CTagsOfPreprocessedSource, ctx.Arduifines = parser.Parse(ctx.CTagsOutput, ctx.Sketch.MainFile) parser.FixCLinkageTagsDeclarations(ctx.CTagsOfPreprocessedSource) protos, line := parser.GeneratePrototypes() diff --git a/legacy/builder/phases/libraries_builder.go b/legacy/builder/phases/libraries_builder.go index a5253f76303..b42256f4ef9 100644 --- a/legacy/builder/phases/libraries_builder.go +++ b/legacy/builder/phases/libraries_builder.go @@ -44,6 +44,10 @@ func (s *LibrariesBuilder) Run(ctx *types.Context) error { return errors.WithStack(err) } + if len(ctx.Arduifines) > 0 { + ctx.GetLogger().Println(constants.LOG_LEVEL_DEBUG, tr("Additional compiler options provided by user sketch: {0}"), ctx.Arduifines) + } + objectFiles, err := compileLibraries(ctx, libs, librariesBuildPath, buildProperties, includes) if err != nil { return errors.WithStack(err) diff --git a/legacy/builder/test/builder_test.go b/legacy/builder/test/builder_test.go index 7bc35b46a39..641677c3953 100644 --- a/legacy/builder/test/builder_test.go +++ b/legacy/builder/test/builder_test.go @@ -43,6 +43,25 @@ func prepareBuilderTestContext(t *testing.T, sketchPath *paths.Path, fqbn string } } +func TestBuilderArduifine(t *testing.T) { + DownloadCoresAndToolsAndLibraries(t) + + ctx := prepareBuilderTestContext(t, paths.New("sketch_arduifine", "sketch_arduifine.ino"), "arduino:avr:uno") + ctx.DebugLevel = 10 + + buildPath := SetupBuildPath(t, ctx) + defer buildPath.RemoveAll() + + // Run builder + command := builder.Builder{} + err := command.Run(ctx) + NoError(t, err) + + exist, err := buildPath.Join("sketch_arduifine.ino.hex").ExistCheck() + NoError(t, err) + require.True(t, exist) +} + func TestBuilderEmptySketch(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) diff --git a/legacy/builder/test/libraries/lib4arduifine/lib4arduifine.cpp b/legacy/builder/test/libraries/lib4arduifine/lib4arduifine.cpp new file mode 100644 index 00000000000..1baa30fc21b --- /dev/null +++ b/legacy/builder/test/libraries/lib4arduifine/lib4arduifine.cpp @@ -0,0 +1,8 @@ + +#include + +#if MYVERSION == 1337 +#pragma message "arduifine: MYVERSION is correctly defined" +#else +#error arduifine: MYVERSION is not correctly defined +#endif diff --git a/legacy/builder/test/libraries/lib4arduifine/lib4arduifine.h b/legacy/builder/test/libraries/lib4arduifine/lib4arduifine.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/legacy/builder/test/libraries_loader_test.go b/legacy/builder/test/libraries_loader_test.go index 9bed71806a5..7d5fdd5c2be 100644 --- a/legacy/builder/test/libraries_loader_test.go +++ b/legacy/builder/test/libraries_loader_test.go @@ -66,7 +66,7 @@ func TestLoadLibrariesAVR(t *testing.T) { require.True(t, Abs(t, paths.New("libraries")).EquivalentTo(librariesFolders[2].Path)) libs := extractLibraries(ctx) - require.Equal(t, 24, len(libs)) + require.Equal(t, 25, len(libs)) sort.Sort(ByLibraryName(libs)) @@ -176,7 +176,7 @@ func TestLoadLibrariesSAM(t *testing.T) { require.True(t, Abs(t, paths.New("libraries")).EquivalentTo(librariesFolders[2].Path)) libraries := extractLibraries(ctx) - require.Equal(t, 22, len(libraries)) + require.Equal(t, 23, len(libraries)) sort.Sort(ByLibraryName(libraries)) diff --git a/legacy/builder/test/sketch_arduifine/sketch_arduifine.ino b/legacy/builder/test/sketch_arduifine/sketch_arduifine.ino new file mode 100644 index 00000000000..6ae6b4a7306 --- /dev/null +++ b/legacy/builder/test/sketch_arduifine/sketch_arduifine.ino @@ -0,0 +1,7 @@ + +#include + +const char* ARDUIFINEtest = "MYVERSION = 1337"; + +void setup() {} +void loop() {} diff --git a/legacy/builder/types/context.go b/legacy/builder/types/context.go index a3f8ee98626..4d98823aa9c 100644 --- a/legacy/builder/types/context.go +++ b/legacy/builder/types/context.go @@ -177,6 +177,11 @@ type Context struct { // The provided source data is used instead of reading it from disk. // The keys of the map are paths relative to sketch folder. SourceOverride map[string]string + + // compiler options from ctags extraction + // example: 'char ARDUIFINExxx = "MYLIB_BUFFER_LEN = 1234";' + // example: 'char ARDUINOGLOBALyyy = "-DMYLIB_BUFFER2_LEN=1234 -free -fipa-pta";' + Arduifines string } // ExecutableSectionSize represents a section of the executable output file