├── .github └── workflows │ └── sync-labels.yml ├── .travis.yml ├── ArduinoDiagnosticConsumer.cpp ├── ArduinoDiagnosticConsumer.hpp ├── CodeCompletion.cpp ├── CodeCompletion.hpp ├── CommandLine.cpp ├── CommandLine.hpp ├── Config.hpp ├── IdentifiersList.cpp ├── IdentifiersList.hpp ├── JsonImpl.hpp ├── README.md ├── arduino_sources_gpg_pubkey.asc ├── arduino_sources_gpg_pubkey.key ├── json.hpp ├── main.cpp ├── package-arduino-preprocessor.bash ├── testsuite ├── .gitignore ├── run_tests.sh ├── term.sh └── testdata │ ├── test_blink_avr.cpp │ ├── test_blink_samd.cpp │ └── test_issue_7.cpp └── utils.hpp /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md 2 | name: Sync Labels 3 | 4 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 5 | on: 6 | push: 7 | paths: 8 | - ".github/workflows/sync-labels.ya?ml" 9 | - ".github/label-configuration-files/*.ya?ml" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/sync-labels.ya?ml" 13 | - ".github/label-configuration-files/*.ya?ml" 14 | schedule: 15 | # Run daily at 8 AM UTC to sync with changes to shared label configurations. 16 | - cron: "0 8 * * *" 17 | workflow_dispatch: 18 | repository_dispatch: 19 | 20 | env: 21 | CONFIGURATIONS_FOLDER: .github/label-configuration-files 22 | CONFIGURATIONS_ARTIFACT: label-configuration-files 23 | 24 | jobs: 25 | check: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v2 31 | 32 | - name: Download JSON schema for labels configuration file 33 | id: download-schema 34 | uses: carlosperate/download-file-action@v1 35 | with: 36 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json 37 | location: ${{ runner.temp }}/label-configuration-schema 38 | 39 | - name: Install JSON schema validator 40 | run: | 41 | sudo npm install \ 42 | --global \ 43 | ajv-cli \ 44 | ajv-formats 45 | 46 | - name: Validate local labels configuration 47 | run: | 48 | # See: https://github.com/ajv-validator/ajv-cli#readme 49 | ajv validate \ 50 | --all-errors \ 51 | -c ajv-formats \ 52 | -s "${{ steps.download-schema.outputs.file-path }}" \ 53 | -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" 54 | 55 | download: 56 | needs: check 57 | runs-on: ubuntu-latest 58 | 59 | strategy: 60 | matrix: 61 | filename: 62 | # Filenames of the shared configurations to apply to the repository in addition to the local configuration. 63 | # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels 64 | - universal.yml 65 | - tooling.yml 66 | 67 | steps: 68 | - name: Download 69 | uses: carlosperate/download-file-action@v1 70 | with: 71 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} 72 | 73 | - name: Pass configuration files to next job via workflow artifact 74 | uses: actions/upload-artifact@v2 75 | with: 76 | path: | 77 | *.yaml 78 | *.yml 79 | if-no-files-found: error 80 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 81 | 82 | sync: 83 | needs: download 84 | runs-on: ubuntu-latest 85 | 86 | steps: 87 | - name: Set environment variables 88 | run: | 89 | # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable 90 | echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" 91 | 92 | - name: Determine whether to dry run 93 | id: dry-run 94 | if: > 95 | github.event_name == 'pull_request' || 96 | ( 97 | ( 98 | github.event_name == 'push' || 99 | github.event_name == 'workflow_dispatch' 100 | ) && 101 | github.ref != format('refs/heads/{0}', github.event.repository.default_branch) 102 | ) 103 | run: | 104 | # Use of this flag in the github-label-sync command will cause it to only check the validity of the 105 | # configuration. 106 | echo "::set-output name=flag::--dry-run" 107 | 108 | - name: Checkout repository 109 | uses: actions/checkout@v2 110 | 111 | - name: Download configuration files artifact 112 | uses: actions/download-artifact@v2 113 | with: 114 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 115 | path: ${{ env.CONFIGURATIONS_FOLDER }} 116 | 117 | - name: Remove unneeded artifact 118 | uses: geekyeggo/delete-artifact@v1 119 | with: 120 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 121 | 122 | - name: Merge label configuration files 123 | run: | 124 | # Merge all configuration files 125 | shopt -s extglob 126 | cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" 127 | 128 | - name: Install github-label-sync 129 | run: sudo npm install --global github-label-sync 130 | 131 | - name: Sync labels 132 | env: 133 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 134 | run: | 135 | # See: https://github.com/Financial-Times/github-label-sync 136 | github-label-sync \ 137 | --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ 138 | ${{ steps.dry-run.outputs.flag }} \ 139 | ${{ github.repository }} 140 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | install: 4 | - ./package-arduino-preprocessor.bash 5 | - cd testsuite 6 | 7 | script: 8 | - ./run_tests.sh 9 | 10 | -------------------------------------------------------------------------------- /ArduinoDiagnosticConsumer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #include 31 | 32 | #include "ArduinoDiagnosticConsumer.hpp" 33 | #include "CommandLine.hpp" 34 | #include "JsonImpl.hpp" 35 | #include "utils.hpp" 36 | 37 | using namespace clang; 38 | 39 | void ArduinoDiagnosticConsumer::collectUndeclaredIdentifiersIn(IdentifiersList &list) { 40 | undeclaredIdentifiersList = &list; 41 | } 42 | 43 | void ArduinoDiagnosticConsumer::outputJsonDiagnosticsTo(raw_ostream &out) { 44 | jsonDiagnosticOutput = &out; 45 | } 46 | 47 | void ArduinoDiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level level, const Diagnostic& info) { 48 | // DiagnosticConsumer::HandleDiagnostic(level, info); 49 | 50 | if (undeclaredIdentifiersList && level == DiagnosticsEngine::Level::Error) { 51 | const SourceManager &sm = info.getSourceManager(); 52 | const SourceLocation &loc = info.getLocation(); 53 | const SourceLocation &sl = sm.getSpellingLoc(loc); 54 | const char *presumedFilename = sm.getPresumedLoc(sl).getFilename(); 55 | if (debugOutput) { 56 | outs() << sm.getSpellingLineNumber(sl) << ":" << sm.getSpellingColumnNumber(sl) << " ("; 57 | outs() << presumedFilename << ") "; 58 | } 59 | 60 | if (!cStrEndsWith(presumedFilename, ".ino")) { 61 | if (debugOutput) { 62 | outs() << "Ignoring non .ino source file\n"; 63 | } 64 | return; 65 | } 66 | 67 | unsigned id = info.getID(); 68 | if (id == 3441 || id == 3442 /* use of undeclared identifier */) { 69 | // It seems that the only way to retrieve the undeclared symbol 70 | // is to print it as a localization string. 71 | const char *fmt = "%0"; 72 | SmallString<100> outArg; 73 | info.FormatDiagnostic(fmt, fmt + 2, outArg); 74 | // we should also remove quotes as well... 75 | StringRef identifier = outArg.substr(1, outArg.size() - 2); 76 | if (debugOutput) { 77 | outs() << "Found undeclared identifier '" << identifier << "'\n"; 78 | } 79 | 80 | // Save the identifier position for later processing 81 | IdentifierLocation *m = new IdentifierLocation; 82 | m->location = FullSourceLoc(loc, sm); 83 | m->identifier = identifier.str(); 84 | undeclaredIdentifiersList->push_front(m); 85 | return; 86 | } 87 | 88 | } 89 | 90 | if (jsonDiagnosticOutput) { 91 | const SourceManager &sm = info.getSourceManager(); 92 | 93 | SmallString<100> message; 94 | info.FormatDiagnostic(message); 95 | 96 | json data = json{ 97 | {"location", encode(sm, info.getLocation())}, 98 | {"message", encode(message)}, 99 | {"ranges", encode(sm, info.getRanges())}, 100 | {"hints", encode(sm, info.getFixItHints())}, 101 | }; 102 | *jsonDiagnosticOutput << data.dump() << "\n"; 103 | } 104 | 105 | if (debugOutput) { 106 | SmallString<100> outStr; 107 | info.FormatDiagnostic(outStr); 108 | outs() << "(" << info.getID() << ") " << outStr << "\n"; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /ArduinoDiagnosticConsumer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #pragma once 31 | 32 | #include 33 | 34 | #include "IdentifiersList.hpp" 35 | 36 | using namespace clang; 37 | using namespace llvm; 38 | 39 | class ArduinoDiagnosticConsumer : public DiagnosticConsumer { 40 | public: 41 | 42 | void collectUndeclaredIdentifiersIn(IdentifiersList &list); 43 | 44 | void outputJsonDiagnosticsTo(raw_ostream &out); 45 | 46 | private: 47 | IdentifiersList *undeclaredIdentifiersList = nullptr; 48 | raw_ostream *jsonDiagnosticOutput = nullptr; 49 | 50 | void HandleDiagnostic(DiagnosticsEngine::Level level, const Diagnostic& info) override; 51 | }; 52 | -------------------------------------------------------------------------------- /CodeCompletion.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | 42 | #include "CodeCompletion.hpp" 43 | #include "CommandLine.hpp" 44 | #include "utils.hpp" 45 | #include "JsonImpl.hpp" 46 | 47 | using namespace clang; 48 | using namespace llvm; 49 | using namespace std; 50 | 51 | class CustomCodeCompleteConsumer : public CodeCompleteConsumer { 52 | CodeCompletionTUInfo TUInfo; 53 | json output; 54 | SourceManager &sm; 55 | 56 | public: 57 | 58 | CustomCodeCompleteConsumer(const CodeCompleteOptions &opts, SourceManager &sm) : CodeCompleteConsumer(opts, false), 59 | TUInfo(std::make_shared()), output(json::array()), sm(sm) { 60 | } 61 | 62 | void ProcessCodeCompleteResults(Sema &s, CodeCompletionContext ctx, CodeCompletionResult *res, unsigned n) override { 63 | for (unsigned i = 0; i != n; ++i) { 64 | CXAvailabilityKind avail = res[i].Availability; 65 | if (avail != CXAvailabilityKind::CXAvailability_Available && avail != CXAvailabilityKind::CXAvailability_Deprecated) { 66 | // Skip definitions that are not usable 67 | continue; 68 | } 69 | 70 | string ccStr = ""; 71 | raw_string_ostream OS(ccStr); 72 | CodeCompletionString *ccs = res[i].CreateCodeCompletionString(s, ctx, getAllocator(), TUInfo, includeBriefComments()); 73 | //outs() << encode(res[i], ccs, sm).dump(2) << "\n"; 74 | output.push_back(encode(res[i], ccs, sm)); 75 | } 76 | 77 | } 78 | 79 | /* 80 | void ProcessOverloadCandidates(Sema &s, unsigned currArg, OverloadCandidate *candidates, unsigned n) { 81 | } 82 | */ 83 | 84 | virtual CodeCompletionAllocator &getAllocator() override { 85 | return TUInfo.getAllocator(); 86 | } 87 | 88 | virtual CodeCompletionTUInfo &getCodeCompletionTUInfo() override { 89 | return TUInfo; 90 | } 91 | 92 | json *GetJSON() { 93 | return &output; 94 | } 95 | }; 96 | 97 | int FindRealLineForCodeCompletion(string &code, string &filename, int line) { 98 | int curr = 1; 99 | int real = 1; 100 | bool inFile = false; 101 | for (string &l : split(code, '\n')) { 102 | real++; 103 | curr++; 104 | if (startsWith(l, "# ") || startsWith(l, "#line ")) { 105 | vector fields = split(l, ' '); 106 | int marker; 107 | if (stringToInt(fields[1], &marker)) { 108 | curr = marker; 109 | } 110 | if (fields[2].find(filename) != string::npos) { 111 | inFile = true; 112 | } else { 113 | inFile = false; 114 | } 115 | } 116 | 117 | if (inFile && curr == line) { 118 | if (debugOutput) { 119 | cerr << "Code-completions at line " << real << "\n"; 120 | } 121 | return real; 122 | } 123 | } 124 | 125 | // Not found... fallback to input line 126 | if (debugOutput) { 127 | cerr << "Sorry! source code line not found...\n"; 128 | } 129 | return -1; 130 | } 131 | 132 | void DoCodeCompletion(const string &filename, const string &code, int line, int col) { 133 | CompilerInstance ci; 134 | ci.createDiagnostics(); 135 | 136 | // Hide diagnostics 137 | ci.getDiagnostics().setClient(new IgnoringDiagConsumer()); 138 | 139 | shared_ptr tOpts = make_shared(); 140 | tOpts->Triple = sys::getDefaultTargetTriple(); 141 | ci.setTarget(TargetInfo::CreateTargetInfo(ci.getDiagnostics(), tOpts)); 142 | 143 | LangOptions &lOpts = ci.getLangOpts(); 144 | lOpts.CPlusPlus = true; 145 | lOpts.CPlusPlus11 = true; 146 | lOpts.Bool = true; 147 | lOpts.GNUMode = true; 148 | 149 | ci.createFileManager(); 150 | ci.createSourceManager(ci.getFileManager()); 151 | 152 | CodeCompleteOptions ccOpts; 153 | ccOpts.IncludeMacros = 1; 154 | ccOpts.IncludeCodePatterns = 1; 155 | ccOpts.IncludeGlobals = 1; 156 | ccOpts.IncludeBriefComments = 1; 157 | CustomCodeCompleteConsumer *ccConsumer = new CustomCodeCompleteConsumer(ccOpts, ci.getSourceManager()); 158 | ci.setCodeCompletionConsumer(ccConsumer); 159 | 160 | FrontendOptions& fOpts = ci.getFrontendOpts(); 161 | fOpts.Inputs.push_back(FrontendInputFile(filename, InputKind::IK_CXX)); 162 | fOpts.CodeCompletionAt.FileName = filename; 163 | fOpts.CodeCompletionAt.Line = line; 164 | fOpts.CodeCompletionAt.Column = col; 165 | 166 | unique_ptr buff(MemoryBuffer::getMemBuffer(code, filename)); 167 | 168 | PreprocessorOptions& pOpts = ci.getPreprocessorOpts(); 169 | pOpts.clearRemappedFiles(); 170 | pOpts.addRemappedFile(filename, buff.release()); 171 | 172 | SyntaxOnlyAction action; 173 | if (action.BeginSourceFile(ci, ci.getFrontendOpts().Inputs[0])) { 174 | action.Execute(); 175 | action.EndSourceFile(); 176 | } 177 | 178 | outs() << ccConsumer->GetJSON()->dump(); 179 | } 180 | -------------------------------------------------------------------------------- /CodeCompletion.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #pragma once 31 | 32 | using namespace std; 33 | 34 | int FindRealLineForCodeCompletion(string &code, string &filename, int line); 35 | 36 | void DoCodeCompletion(const string &sourceFilename, const string &code, int line, int col); 37 | -------------------------------------------------------------------------------- /CommandLine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #include "CommandLine.hpp" 31 | #include "Config.hpp" 32 | #include "utils.hpp" 33 | 34 | #include 35 | #include 36 | 37 | bool debugOutput; 38 | bool outputDiagnostics; 39 | bool outputOnlyNeededPrototypes; 40 | bool outputPreprocessedSketch = true; 41 | 42 | // Code completion parameters 43 | bool outputCodeCompletions; 44 | string codeCompleteFilename; 45 | int codeCompleteLine; 46 | int codeCompleteCol; 47 | 48 | static cl::OptionCategory arduinoToolCategory("Arduino options"); 49 | // TODO: add complete help 50 | static cl::extrahelp arduinoHelp("\n" 51 | "arduino-preprocessor is a command-line utility based on LLVM and clang tooling.\n" 52 | "\n" 53 | ); 54 | static cl::extrahelp commonHelp(CommonOptionsParser::HelpMessage); 55 | static cl::opt debugOutputOpt("debug"); 56 | static cl::opt outputOnlyNeededPrototypesOpt("output-only-needed-prototypes"); 57 | static cl::opt outputDiagnosticsOpt("output-diagnostics"); 58 | static cl::opt outputCodeCompletionsOpt("output-code-completions"); 59 | 60 | static void printVersion() { 61 | outs() << "Arduino (https://www.arduino.cc/):\n"; 62 | outs() << " arduino-preprocessor version " VERSION "\n"; 63 | } 64 | 65 | CommonOptionsParser doCommandLineParsing(int argc, const char **argv) { 66 | debugOutputOpt.setCategory(arduinoToolCategory); 67 | debugOutputOpt.setInitialValue(false); 68 | debugOutputOpt.setDescription("Print debugging messages from Arduino preprocessor"); 69 | 70 | outputOnlyNeededPrototypesOpt.setCategory(arduinoToolCategory); 71 | outputOnlyNeededPrototypesOpt.setInitialValue(false); 72 | outputOnlyNeededPrototypesOpt.setDescription("Output a prototype only if a forward declaration is needed (experimental)"); 73 | 74 | outputDiagnosticsOpt.setCategory(arduinoToolCategory); 75 | outputDiagnosticsOpt.setInitialValue(false); 76 | outputDiagnosticsOpt.setDescription("Output diagnostics (warnings/errors) in json format"); 77 | 78 | outputCodeCompletionsOpt.setCategory(arduinoToolCategory); 79 | outputCodeCompletionsOpt.setInitialValue(""); 80 | outputCodeCompletionsOpt.setDescription( 81 | "Output code completions (suggestions) in json format.\n" 82 | "This option requires the cursor position in the format \"filename:line:col\""); 83 | 84 | cl::AddExtraVersionPrinter(printVersion); 85 | 86 | CommonOptionsParser optParser(argc, argv, arduinoToolCategory); 87 | 88 | /* Parse outputCodeCompletion parameter */ 89 | if (outputCodeCompletionsOpt.getValue() != "") { 90 | vector spl = split(outputCodeCompletionsOpt.getValue(), ':'); 91 | if (spl.size() != 3) { 92 | cerr << "code completion requires parameter in the form \"filename:line:col\"\n"; 93 | exit(1); 94 | } 95 | codeCompleteFilename = spl[0]; 96 | if (!stringToInt(spl[1], &codeCompleteLine)) { 97 | cerr << "code completion requires 'line' to be a positive integer parameter in the form \"filename:line:col\"\n"; 98 | exit(1); 99 | } 100 | if (!stringToInt(spl[2], &codeCompleteCol)) { 101 | cerr << "code completion requires 'col' to be a positive integer parameter in the form \"filename:line:col\"\n"; 102 | exit(1); 103 | } 104 | outputCodeCompletions = true; 105 | } 106 | 107 | debugOutput = debugOutputOpt.getValue(); 108 | outputOnlyNeededPrototypes = outputOnlyNeededPrototypesOpt.getValue(); 109 | outputDiagnostics = outputDiagnosticsOpt.getValue(); 110 | if (outputDiagnostics || outputCodeCompletions) { 111 | outputPreprocessedSketch = false; 112 | } 113 | return optParser; 114 | } -------------------------------------------------------------------------------- /CommandLine.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #pragma once 31 | 32 | #include 33 | #include 34 | 35 | using namespace clang; 36 | using namespace clang::tooling; 37 | using namespace llvm; 38 | using namespace std; 39 | 40 | extern bool debugOutput; 41 | extern bool outputOnlyNeededPrototypes; 42 | extern bool outputDiagnostics; 43 | extern bool outputPreprocessedSketch; 44 | 45 | // Code completion parameters 46 | extern bool outputCodeCompletions; 47 | extern string codeCompleteFilename; 48 | extern int codeCompleteLine; 49 | extern int codeCompleteCol; 50 | 51 | CommonOptionsParser doCommandLineParsing(int argc, const char **argv); 52 | -------------------------------------------------------------------------------- /Config.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #pragma once 31 | 32 | #define VERSION "0.1.5" 33 | -------------------------------------------------------------------------------- /IdentifiersList.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #include "IdentifiersList.hpp" 31 | 32 | void IdentifiersList::dump(ostream &out) { 33 | out << "Undeclared identifiers:\n"; 34 | for (IdentifierLocation *m : * this) { 35 | const FullSourceLoc &sl = m->location.getSpellingLoc(); 36 | out << " " << sl.getSpellingLineNumber() << ":" << sl.getSpellingColumnNumber(); 37 | out << " " << m->identifier << "\n"; 38 | } 39 | } 40 | 41 | IdentifierLocation *IdentifiersList::findFirst(string name) { 42 | for (IdentifierLocation *m : * this) { 43 | if (m->identifier == name) 44 | return m; 45 | } 46 | return nullptr; 47 | } 48 | -------------------------------------------------------------------------------- /IdentifiersList.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #pragma once 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | using namespace clang; 39 | using namespace std; 40 | 41 | typedef struct { 42 | FullSourceLoc location; 43 | string identifier; 44 | } IdentifierLocation; 45 | 46 | class IdentifiersList : public list { 47 | public: 48 | IdentifierLocation *findFirst(string name); 49 | void dump(ostream &out); 50 | }; 51 | -------------------------------------------------------------------------------- /JsonImpl.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #pragma once 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace clang; 37 | using namespace std; 38 | 39 | #define JSON_NOEXCEPTION 40 | #include "json.hpp" 41 | #include "utils.hpp" 42 | #include "clang/include/clang/Sema/CodeCompleteConsumer.h" 43 | using json = nlohmann::json; 44 | 45 | template 46 | inline string encode(const SmallString &s) { 47 | return s.str().str(); 48 | } 49 | 50 | inline json encode(const SourceManager &sm, const SourceLocation &loc) { 51 | PresumedLoc presumed = sm.getPresumedLoc(loc); 52 | stringstream pos; 53 | pos << presumed.getLine() << ":" << presumed.getColumn(); 54 | std::string filename(presumed.getFilename()); 55 | filename = quoteCppString(filename); 56 | return json{ 57 | {"file", filename.c_str()}, 58 | {"pos", pos.str()}}; 59 | } 60 | 61 | inline json encode(const SourceManager &sm, const CharSourceRange &range) { 62 | if (range.isInvalid()) { 63 | return json{}; 64 | } 65 | return json{ 66 | {"begin", encode(sm, range.getBegin())}, 67 | {"end", encode(sm, range.getEnd())}}; 68 | } 69 | 70 | inline json encode(const SourceManager &sm, const FixItHint &hint) { 71 | return json{ 72 | {"before_previous", hint.BeforePreviousInsertions}, 73 | {"text", hint.CodeToInsert}, 74 | {"insert_from", encode(sm, hint.InsertFromRange)}, 75 | {"remove", encode(sm, hint.RemoveRange)}}; 76 | } 77 | 78 | template 79 | inline json encode(const SourceManager &sm, const ArrayRef &array) { 80 | json res = json::array(); 81 | for (T elem : array) { 82 | res.push_back(encode(sm, elem)); 83 | } 84 | return res; 85 | } 86 | 87 | inline json encode(const CodeCompletionString *ccs) { 88 | json chunks = json::array(); 89 | for (const CodeCompletionString::Chunk &c : *ccs) { 90 | switch (c.Kind) { 91 | case CodeCompletionString::ChunkKind::CK_Colon: 92 | chunks.push_back(json{ 93 | {"t", ":"} 94 | }); 95 | break; 96 | case CodeCompletionString::ChunkKind::CK_Comma: 97 | chunks.push_back(json{ 98 | {"t", ","} 99 | }); 100 | break; 101 | case CodeCompletionString::ChunkKind::CK_CurrentParameter: 102 | chunks.push_back(json{ 103 | {"current_param", c.Text} 104 | }); 105 | break; 106 | case CodeCompletionString::ChunkKind::CK_Equal: 107 | chunks.push_back(json{ 108 | {"t", "="} 109 | }); 110 | break; 111 | case CodeCompletionString::ChunkKind::CK_HorizontalSpace: 112 | chunks.push_back(json{ 113 | {"t", " "} 114 | }); 115 | break; 116 | case CodeCompletionString::ChunkKind::CK_Informative: 117 | chunks.push_back(json{ 118 | {"info", c.Text} 119 | }); 120 | break; 121 | case CodeCompletionString::ChunkKind::CK_LeftAngle: 122 | chunks.push_back(json{ 123 | {"t", "<"} 124 | }); 125 | break; 126 | case CodeCompletionString::ChunkKind::CK_LeftBrace: 127 | chunks.push_back(json{ 128 | {"t", "{"} 129 | }); 130 | break; 131 | case CodeCompletionString::ChunkKind::CK_LeftBracket: 132 | chunks.push_back(json{ 133 | {"t", "["} 134 | }); 135 | break; 136 | case CodeCompletionString::ChunkKind::CK_LeftParen: 137 | chunks.push_back(json{ 138 | {"t", "("} 139 | }); 140 | break; 141 | case CodeCompletionString::ChunkKind::CK_Optional: 142 | chunks.push_back(json{ 143 | {"optional", encode(c.Optional)} 144 | }); 145 | break; 146 | case CodeCompletionString::ChunkKind::CK_Placeholder: 147 | chunks.push_back(json{ 148 | {"placeholder", c.Text} 149 | }); 150 | break; 151 | case CodeCompletionString::ChunkKind::CK_ResultType: 152 | chunks.push_back(json{ 153 | {"res", c.Text} 154 | }); 155 | break; 156 | case CodeCompletionString::ChunkKind::CK_RightAngle: 157 | chunks.push_back(json{ 158 | {"t", ">"} 159 | }); 160 | break; 161 | case CodeCompletionString::ChunkKind::CK_RightBrace: 162 | chunks.push_back(json{ 163 | {"t", "}"} 164 | }); 165 | break; 166 | case CodeCompletionString::ChunkKind::CK_RightBracket: 167 | chunks.push_back(json{ 168 | {"t", "]"} 169 | }); 170 | break; 171 | case CodeCompletionString::ChunkKind::CK_RightParen: 172 | chunks.push_back(json{ 173 | {"t", ")"} 174 | }); 175 | break; 176 | case CodeCompletionString::ChunkKind::CK_SemiColon: 177 | chunks.push_back(json{ 178 | {"t", ";"} 179 | }); 180 | break; 181 | case CodeCompletionString::ChunkKind::CK_Text: 182 | chunks.push_back(json{ 183 | {"t", c.Text} 184 | }); 185 | break; 186 | case CodeCompletionString::ChunkKind::CK_TypedText: 187 | chunks.push_back(json{ 188 | {"typedtext", c.Text} 189 | }); 190 | break; 191 | case CodeCompletionString::ChunkKind::CK_VerticalSpace: 192 | chunks.push_back(json{ 193 | {"t", "\n"} 194 | }); 195 | break; 196 | }; 197 | } 198 | 199 | json res = json::object(); 200 | res["chunks"] = chunks; 201 | // This seems to be redundant 202 | //if (ccs->getTypedText()) { 203 | // res["typedtext"] = ccs->getTypedText(); 204 | //} 205 | if (ccs->getBriefComment()) { 206 | res["brief"] = ccs->getBriefComment(); 207 | } 208 | return res; 209 | } 210 | 211 | inline json encode(const CodeCompletionResult &cc, const CodeCompletionString *ccs, const SourceManager &sm) { 212 | json res = json{ 213 | {"completion", encode(ccs)} 214 | }; 215 | 216 | /* XXX: This makes a memory corruption and core-dumps (at least on Windows), something to check... 217 | 218 | // Extract doxygen comment if available 219 | ASTContext& ast = cc.Declaration->getASTContext(); 220 | RawComment* comment = ast.getRawCommentForDeclNoCache(cc.Declaration); 221 | if (comment) { 222 | res["doxy"] = comment->getRawText(sm).str(); 223 | } 224 | */ 225 | 226 | switch (cc.Kind) { 227 | case CodeCompletionResult::RK_Declaration: 228 | { 229 | SourceLocation loc = cc.Declaration->getLocation(); 230 | PresumedLoc presumedLoc = sm.getPresumedLoc(loc); 231 | std::string filename(presumedLoc.getFilename()); 232 | filename = quoteCppString(filename); 233 | res["location"] = filename.c_str(); 234 | res["type"] = cc.Declaration->getDeclKindName(); 235 | 236 | // For each parameter extract type and name 237 | if (const FunctionDecl * d = dyn_cast(cc.Declaration)) { 238 | json p = json::array(); 239 | ArrayRef params = d->parameters(); 240 | for (size_t i = 0; i < params.size(); i++) { 241 | p.push_back(json{ 242 | {"type", params[i]->getType().getAsString()}, 243 | {"name", params[i]->getNameAsString()}, 244 | }); 245 | } 246 | res["parameters"] = p; 247 | } 248 | break; 249 | } 250 | case CodeCompletionResult::RK_Keyword: 251 | { 252 | res["type"] = "Keyword"; 253 | break; 254 | } 255 | case CodeCompletionResult::RK_Pattern: 256 | { 257 | res["type"] = "Pattern"; 258 | break; 259 | } 260 | case CodeCompletionResult::RK_Macro: 261 | { 262 | res["type"] = "Macro"; 263 | break; 264 | } 265 | }; 266 | return res; 267 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino Sketch preprocessor 2 | 3 | [![Build Status](https://travis-ci.org/arduino/arduino-preprocessor.svg?branch=master)](https://travis-ci.org/arduino/arduino-preprocessor) 4 | 5 | This repository contains the source code of the Arduino Sketch preprocessor. 6 | The main purpose of this tool is to parse an Arduino Sketch (usually made with the [Arduino IDE](https://github.com/arduino/Arduino)) and convert it into a valid C++ source code. 7 | 8 | This tool is based on the Clang/LLVM C++11 parser and provides also some useful features used in the Arduino IDE like: 9 | 10 | * Diagnostics output in JSON format 11 | * Code autocomplete suggestions output in JSON format 12 | 13 | ## About the Arduino Sketch (.ino) preprocessing 14 | 15 | The Arduino Sketch is a simplified C++ source code that can be composed by many files with the extension `.ino`. 16 | To convert the Sketch into a valid C++ file, a number of actions are needed: 17 | 18 | * If the Sketch is composed by many `.ino` files, those files are concatenated together into a single `.ino.cpp` file 19 | * An `#include ` is added at the beginning of the Sketch if not already present 20 | * All needed libraries are detected and include paths are discovered 21 | * All `#include` directives are replaced with the actual content of the files included (this is made with a run of `gcc` or another command line compatible compiler with the `-E` flag) 22 | 23 | and finally: 24 | 25 | * The resulting file is preprocessed to automatically add missing function prototypes (forward declarations) 26 | 27 | The `arduino-preprocessor` tool takes care to handle this last step. 28 | 29 | ## Usage 30 | 31 | ``` 32 | ./arduino-preprocessor [-output-only-needed-prototypes] 33 | [-output-code-completions=file:line:col] 34 | [-output-diagnostics] 35 | [-help] [-version] 36 | [-debug] 37 | -- 38 | [extra compiler options] 39 | ``` 40 | 41 | The only mandatory parameter is the name of the file to be processed and the terminating double dash `--`. Every parameter after the `--` is passed as-is to the clang compiler backend. 42 | 43 | The tool outputs the processed source code on the standard output. 44 | 45 | For example the file `t.cpp`: 46 | 47 | ``` 48 | void a() { 49 | b(); 50 | } 51 | 52 | void b() { 53 | } 54 | ``` 55 | 56 | will be processed as: 57 | 58 | ``` 59 | $ ./arduino-preprocessor t.cpp -- 60 | #line 2 "/home/cmaglie/Workspace/arduino-preprocessor/t.cpp" 61 | void a(); 62 | #line 6 "/home/cmaglie/Workspace/arduino-preprocessor/t.cpp" 63 | void b(); 64 | #line 2 "/home/cmaglie/Workspace/arduino-preprocessor/t.cpp" 65 | void a() { 66 | b(); 67 | } 68 | 69 | void b() { 70 | } 71 | ``` 72 | 73 | ### Option `-output-only-needed-prototypes` 74 | 75 | Note: this option is **very experimental** and barely tested, **use at your own risk**. 76 | 77 | By default `arduino-preprocessor` produces the forward prototypes of **all** functions, but if the option `-output-only-needed-prototypes` is used, the output will contains only the **needed** prototypes. If we look at the previous example the result will be: 78 | 79 | ``` 80 | $ ./arduino-preprocessor t.cpp -output-only-needed-prototypes -- 81 | 82 | #line 6 "/home/cmaglie/Workspace/arduino-preprocessor/t.cpp" 83 | void b(); 84 | #line 2 "/home/cmaglie/Workspace/arduino-preprocessor/t.cpp" 85 | void a() { 86 | b(); 87 | } 88 | 89 | void b() { 90 | } 91 | ``` 92 | 93 | ### Option `-output-code-completions=file:line:col` 94 | 95 | Output code completions for the specified file at the specified line in JSON format. `file` should be specified with full path (as reported in the `#line` directives of the processed output). 96 | 97 | `line` and `col` are 1-based indexes (so the first column/line is `1` and not `0`) 98 | 99 | Example: 100 | 101 | ``` 102 | $ ./arduino-preprocessor t.cpp -output-code-completions=/home/cmaglie/Workspace/arduino-preprocessor/t.cpp:3:1 -- 103 | [...big json output follows...] 104 | ``` 105 | 106 | The processed source will **not** be part of the output when this option is enabled. 107 | 108 | ### Option `-output-diagnostics` 109 | 110 | Output diagnostics (errors and warnings) in JSON format. The processed source will **not** be part of the output if this option is enabled. 111 | 112 | ### Option `-debug` 113 | 114 | This option enable debugging output during the processing of the Sketch and a lot of debugging messages are printed. This option should be used when a problem is found to understand what's happening and to produce a better bug-report when filing an issue. 115 | 116 | ### Option `-version` 117 | 118 | Prints the version of the tool 119 | 120 | ### Option `-help` 121 | 122 | Prints a short help 123 | 124 | ## Credits 125 | 126 | This tool use code from the following projects: 127 | 128 | - [Clang/LLVM](http://llvm.org) 129 | - [JSON for Modern C++](https://github.com/nlohmann/json) 130 | -------------------------------------------------------------------------------- /arduino_sources_gpg_pubkey.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: GnuPG v2 3 | 4 | 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 50 | 9aeOcg== 51 | =Orvu 52 | -----END PGP PUBLIC KEY BLOCK----- 53 | -------------------------------------------------------------------------------- /arduino_sources_gpg_pubkey.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/arduino-preprocessor/c29e25ed459cddb9b9a0eec5d77395d382b21925/arduino_sources_gpg_pubkey.key -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | 48 | #include "ArduinoDiagnosticConsumer.hpp" 49 | #include "CommandLine.hpp" 50 | #include "Config.hpp" 51 | #include "CodeCompletion.hpp" 52 | #include "IdentifiersList.hpp" 53 | #include "utils.hpp" 54 | 55 | using namespace clang; 56 | using namespace clang::ast_matchers; 57 | using namespace clang::tooling; 58 | using namespace llvm; 59 | using namespace std; 60 | 61 | IdentifiersList undeclaredIdentifiers; 62 | 63 | Rewriter rewriter; 64 | 65 | class INOPreprocessorMatcherCallback : public MatchFinder::MatchCallback { 66 | bool insertionPointFound = false; 67 | bool firstLineInserted = false; 68 | FullSourceLoc insertionPoint; 69 | PresumedLoc presumedInsertionPoint; 70 | 71 | DeclarationMatcher funcMatcher = functionDecl(isDefinition()).bind("function_decl"); 72 | DeclarationMatcher varMatcher = varDecl().bind("var_decl"); 73 | //StatementMatcher funcCallMatcher = callExpr().bind("function_call"); 74 | public: 75 | 76 | void attachTo(MatchFinder &finder) { 77 | finder.addMatcher(funcMatcher, this); 78 | finder.addMatcher(varMatcher, this); 79 | //finder.addMatcher(funcCallMatcher, &funcDeclaredCB); 80 | } 81 | 82 | void run(const MatchFinder::MatchResult &match) override { 83 | ASTContext *ctx = match.Context; 84 | SourceManager &sm = ctx->getSourceManager(); 85 | 86 | const FunctionDecl *f = match.Nodes.getNodeAs("function_decl"); 87 | if (f) { 88 | FullSourceLoc loc = ctx->getFullLoc(f->getLocStart()); 89 | SourceRange r = f->getSourceRange(); 90 | FullSourceLoc begin = ctx->getFullLoc(r.getBegin()); 91 | FullSourceLoc end = ctx->getFullLoc(r.getEnd()); 92 | 93 | if (debugOutput) { 94 | outs() << "Function " << f->getName() << " declared at "; 95 | outs() << loc.getSpellingLineNumber() << ":" << loc.getSpellingColumnNumber(); 96 | outs() << " (range " << begin.getSpellingLineNumber() << ":" << begin.getSpellingColumnNumber(); 97 | outs() << " to " << end.getSpellingLineNumber() << ":" << end.getSpellingColumnNumber() << ")\n"; 98 | } 99 | 100 | if (dyn_cast(f)) { 101 | if (debugOutput) { 102 | outs() << " Ignored CXX method declaration.\n"; 103 | } 104 | return; 105 | } 106 | 107 | if (dyn_cast(f)) { 108 | if (debugOutput) { 109 | outs() << " Ignored CXX constructor declaration.\n"; 110 | } 111 | return; 112 | } 113 | 114 | if (f->getParentFunctionOrMethod()) { 115 | if (debugOutput) { 116 | outs() << " Function is not top level, ignoring.\n"; 117 | } 118 | return; 119 | } 120 | 121 | //f->dump(); 122 | 123 | detectInsertionPoint(sm, begin, end); 124 | if (!insertionPointFound) { 125 | return; 126 | } 127 | 128 | if (outputOnlyNeededPrototypes) { 129 | // Check if this function is called and needs a forward declaration 130 | IdentifierLocation *und = undeclaredIdentifiers.findFirst(f->getName()); 131 | if (!und) { 132 | if (debugOutput) { 133 | outs() << " This function is not forward-called and do not need a prototype.\n"; 134 | } 135 | return; 136 | } 137 | } 138 | 139 | // Extract line pragma for prototype insertion 140 | writeLineInfo(sm.getPresumedLoc(loc, true)); 141 | 142 | // Extract prototype from function using the pretty printer 143 | // and stopping at the first open curly brace "{" 144 | if (f->isExternC()) { 145 | rewriter.InsertTextAfter(insertionPoint, "extern \"C\" "); 146 | } 147 | string proto; 148 | raw_string_ostream o(proto); 149 | f->print(o); 150 | o.flush(); 151 | proto = proto.substr(0, proto.find_first_of('{') - 1) + ";\n"; 152 | rewriter.InsertTextAfter(insertionPoint, proto); 153 | firstLineInserted = true; 154 | if (debugOutput) { 155 | outs() << " Generated prototype: " << proto; 156 | } 157 | } 158 | 159 | const VarDecl *v = match.Nodes.getNodeAs("var_decl"); 160 | if (v) { 161 | if (v->getParentFunctionOrMethod()) { 162 | //if (debugOutput) { 163 | // outs() << " Variable is not top level, ignoring.\n"; 164 | //} 165 | return; 166 | } 167 | 168 | FullSourceLoc loc = ctx->getFullLoc(v->getLocStart()); 169 | SourceRange r = v->getSourceRange(); 170 | FullSourceLoc begin = ctx->getFullLoc(r.getBegin()); 171 | FullSourceLoc end = ctx->getFullLoc(r.getEnd()); 172 | 173 | if (debugOutput) { 174 | outs() << "Variable " << v->getName() << " declared at "; 175 | outs() << loc.getSpellingLineNumber() << ":" << loc.getSpellingColumnNumber(); 176 | outs() << " (range " << begin.getSpellingLineNumber() << ":" << begin.getSpellingColumnNumber(); 177 | outs() << " to " << end.getSpellingLineNumber() << ":" << end.getSpellingColumnNumber() << ")\n"; 178 | } 179 | 180 | detectInsertionPoint(sm, begin, end); 181 | } 182 | } 183 | 184 | void detectInsertionPoint(SourceManager &sm, FullSourceLoc &begin, FullSourceLoc &end) { 185 | if (insertionPointFound) { 186 | return; 187 | } 188 | 189 | if (undeclaredIdentifiers.empty()) { 190 | //insertionPoint = begin; 191 | //insertionPointFound = true; 192 | //if (debugOutput) { 193 | // outs() << " !! Insertion point found (using the first available position)\n"; 194 | //} 195 | return; 196 | } 197 | 198 | FullSourceLoc first = undeclaredIdentifiers.back()->location; 199 | if (first.isBeforeInTranslationUnitThan(begin)) { 200 | markInsertionPointAsFound(); 201 | return; 202 | } 203 | 204 | if (end.isInvalid()) { 205 | return; 206 | } 207 | 208 | insertionPoint = begin; 209 | presumedInsertionPoint = sm.getPresumedLoc(begin, true); 210 | if (debugOutput) { 211 | outs() << " Insertion point pushed to "; 212 | outs() << begin.getSpellingLineNumber() << ":" << begin.getSpellingColumnNumber() << "\n"; 213 | } 214 | 215 | if (first.isBeforeInTranslationUnitThan(end)) { 216 | markInsertionPointAsFound(); 217 | } 218 | } 219 | 220 | void markInsertionPointAsFound() { 221 | if (debugOutput) { 222 | outs() << " !! Insertion point found at "; 223 | outs() << insertionPoint.getSpellingLineNumber() << ":" << insertionPoint.getSpellingColumnNumber() << "\n"; 224 | } 225 | insertionPointFound = true; 226 | 227 | if (insertionPoint.getSpellingColumnNumber() != 1) { 228 | if (debugOutput) { 229 | outs() << " Insertion point is not at the line beginning -> adding a newline\n"; 230 | } 231 | rewriter.InsertTextAfter(insertionPoint, "\n"); 232 | } 233 | } 234 | 235 | void onEndOfTranslationUnit() override { 236 | if (firstLineInserted) { 237 | writeLineInfo(presumedInsertionPoint); 238 | } 239 | } 240 | 241 | void writeLineInfo(const PresumedLoc &presumed) { 242 | ostringstream lineInfo; 243 | lineInfo << "#line " << presumed.getLine(); 244 | lineInfo << " \"" << presumed.getFilename() << "\"\n"; 245 | std::string lineInfoAsStr = lineInfo.str(); 246 | lineInfoAsStr = quoteCppString(lineInfoAsStr); 247 | rewriter.InsertTextAfter(insertionPoint, lineInfoAsStr); 248 | } 249 | }; 250 | 251 | static string preprocessedSketch; 252 | 253 | class INOPreprocessAction : public ASTFrontendAction { 254 | MatchFinder finder; 255 | INOPreprocessorMatcherCallback funcDeclaredCB; 256 | 257 | public: 258 | 259 | INOPreprocessAction() { 260 | funcDeclaredCB.attachTo(finder); 261 | } 262 | 263 | unique_ptr CreateASTConsumer(CompilerInstance &compiler, StringRef inFile) override { 264 | rewriter.setSourceMgr(compiler.getSourceManager(), compiler.getLangOpts()); 265 | return finder.newASTConsumer(); 266 | } 267 | 268 | virtual void EndSourceFileAction() override { 269 | if (debugOutput) { 270 | ostringstream out; 271 | undeclaredIdentifiers.dump(out); 272 | out.flush(); 273 | outs() << out.str(); 274 | } 275 | 276 | const FileID mainFileID = rewriter.getSourceMgr().getMainFileID(); 277 | const RewriteBuffer *buf = rewriter.getRewriteBufferFor(mainFileID); 278 | if (buf == nullptr) { 279 | // No changes needed, output the source file as-is 280 | auto buff = rewriter.getSourceMgr().getBuffer(mainFileID); 281 | preprocessedSketch = buff->getBuffer().str(); 282 | } else { 283 | preprocessedSketch = string(buf->begin(), buf->end()); 284 | } 285 | } 286 | }; 287 | 288 | int main(int argc, const char **argv) { 289 | CommonOptionsParser optParser = doCommandLineParsing(argc, argv); 290 | ClangTool tool(optParser.getCompilations(), optParser.getSourcePathList()); 291 | 292 | ArduinoDiagnosticConsumer dc; 293 | dc.collectUndeclaredIdentifiersIn(undeclaredIdentifiers); 294 | if (outputDiagnostics) { 295 | dc.outputJsonDiagnosticsTo(outs()); 296 | } 297 | tool.setDiagnosticConsumer(&dc); 298 | 299 | int res = tool.run(newFrontendActionFactory().get()); 300 | 301 | if (outputPreprocessedSketch) { 302 | outs() << preprocessedSketch; 303 | } 304 | 305 | if (outputCodeCompletions) { 306 | int line = FindRealLineForCodeCompletion(preprocessedSketch, codeCompleteFilename, codeCompleteLine); 307 | if (line != -1) { 308 | DoCodeCompletion(optParser.getSourcePathList()[0], preprocessedSketch, line, codeCompleteCol); 309 | } 310 | } 311 | 312 | return res; 313 | } 314 | -------------------------------------------------------------------------------- /package-arduino-preprocessor.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | # Copyright (c) 2017 BCMI LABS SA 3 | # 4 | # This program is free software; you can redistribute it and/or 5 | # modify it under the terms of the GNU General Public License 6 | # as published by the Free Software Foundation; either version 2 7 | # of the License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | ARDUINO_PREPROCESSOR_VERSION=`cat Config.hpp | grep VERSION | sed 's/[^"]*"\([^"]*\)".*/\1/'` 19 | 20 | OUTPUT_VERSION=${ARDUINO_PREPROCESSOR_VERSION} 21 | 22 | # 23 | # Clean up workspace 24 | # ------------------ 25 | # 26 | 27 | rm -rf objdir arduino-preprocessor 28 | mkdir objdir 29 | 30 | # 31 | # Detect OS and download LLVM 32 | # --------------------------- 33 | # 34 | 35 | export OS=`uname -o || uname` 36 | export TARGET_OS=$OS 37 | 38 | START_GROUP=-Wl,--start-group 39 | END_GROUP=-Wl,--end-group 40 | 41 | function fetch_llvm { 42 | fetched=`basename $1` 43 | if [ ! -f "$fetched" ]; then 44 | wget "$1" 45 | fi 46 | if [ ! -f "$fetched.asc" ]; then 47 | wget "$1.asc" 48 | fi 49 | gpg2 --status-fd 1 --no-default-keyring --homedir . --keyring "arduino_sources_gpg_pubkey.key" --trust-model always --verify "$fetched.asc" 50 | 51 | rm -rf clang 52 | mkdir clang 53 | tar xf $fetched -C clang --strip-components=1 54 | } 55 | 56 | #if [[ $CROSS_COMPILE == "mingw" ]] ; then 57 | # 58 | # export CC="i686-w64-mingw32-gcc" 59 | # export CXX="i686-w64-mingw32-g++" 60 | # export CROSS_COMPILE_HOST="i686-w64-mingw32" 61 | # export TARGET_OS="Windows" 62 | # OUTPUT_TAG=i686-w64-mingw32 63 | # 64 | if [[ $OS == "GNU/Linux" ]] ; then 65 | 66 | export MACHINE=`uname -m` 67 | if [[ $MACHINE == "x86_64" ]] ; then 68 | fetch_llvm https://github.com/cmaglie/llvm-clang-build-scripts/releases/download/4.0.0/llvm-clang-4.0.0-ubuntu-14.04.5-x86_64.tar.xz 69 | OUTPUT_TAG=x86_64-pc-linux-gnu 70 | elif [[ $MACHINE == "i686" ]] ; then 71 | OUTPUT_TAG=i686-pc-linux-gnu 72 | fetch_llvm https://github.com/cmaglie/llvm-clang-build-scripts/releases/download/4.0.0/llvm-clang-4.0.0-ubuntu-14.04.5-i686.tar.xz 73 | elif [[ $MACHINE == "armv7l" ]] ; then 74 | OUTPUT_TAG=armhf-pc-linux-gnu 75 | fetch_llvm https://github.com/cmaglie/llvm-clang-build-scripts/releases/download/4.0.0/llvm-clang-4.0.0-linux-arm.tar.xz 76 | else 77 | echo Linux Machine not supported: $MACHINE 78 | exit 1 79 | fi 80 | CXXFLAGS="" 81 | CXX=clang/bin/clang++ 82 | 83 | elif [[ $OS == "Msys" || $OS == "Cygwin" ]] ; then 84 | 85 | fetch_llvm https://github.com/cmaglie/llvm-clang-build-scripts/releases/download/4.0.0/llvm-clang-4.0.0-cygwin-i686.tar.xz 86 | OUTPUT_TAG=i686-pc-cygwin 87 | CXXFLAGS="-Wno-strict-aliasing" 88 | CXX=g++ 89 | 90 | # This is a workaround for building with clang.exe (keeping here, just in case...) 91 | #GCC_VER=$(ls /usr/lib/gcc/$OUTPUT_TAG/) 92 | #GCC_INCLUDE="/usr/lib/gcc/$OUTPUT_TAG/$GCC_VER/include" 93 | #mkdir -p include/bits 94 | #cp $GCC_INCLUDE/c++/$OUTPUT_TAG/bits/c++config.h include/bits 95 | #sed -i "s/^#define _GLIBCXX_USE_FLOAT128 1$/\/\/#define _GLIBCXX_USE_FLOAT128 1/" include/bits/c++config.h 96 | #CXXFLAGS="-Iinclude -I$GCC_INCLUDE/c++ -I$GCC_INCLUDE/c++/$OUTPUT_TAG -I$GCC_INCLUDE/c++/backward -I$GCC_INCLUDE" 97 | 98 | elif [[ $OS == "Darwin" ]] ; then 99 | 100 | #export PATH=/opt/local/libexec/gnubin/:/opt/local/bin:$PATH 101 | export CC="gcc -arch x86_64 -mmacosx-version-min=10.9" 102 | export CXX="g++ -arch x86_64 -mmacosx-version-min=10.9" 103 | OUTPUT_TAG=x86_64-apple-darwin11 104 | export CXXFLAGS="-stdlib=libc++ -std=c++11" 105 | fetch_llvm https://github.com/cmaglie/llvm-clang-build-scripts/releases/download/4.0.0/llvm-clang-4.0.0-macosx-10.9-x86_64.tar.bz2 106 | START_GROUP="" 107 | END_GROUP="" 108 | 109 | else 110 | 111 | echo OS Not supported: $OS 112 | exit 2 113 | 114 | fi 115 | 116 | # 117 | # Build arduino-preprocessor 118 | # -------------------------- 119 | # 120 | 121 | CXXFLAGS="`clang/bin/llvm-config --cxxflags` $CXXFLAGS" 122 | LDFLAGS="`clang/bin/llvm-config --ldflags` -static-libstdc++" 123 | LLVMLIBS=`clang/bin/llvm-config --libs --system-libs` 124 | CLANGLIBS=`ls clang/lib/libclang*.a | sed s/.*libclang/-lclang/ | sed s/.a$//` 125 | SOURCES="main.cpp ArduinoDiagnosticConsumer.cpp CommandLine.cpp IdentifiersList.cpp CodeCompletion.cpp" 126 | $CXX $SOURCES -o objdir/arduino-preprocessor $CXXFLAGS $LDFLAGS $START_GROUP $LLVMLIBS $CLANGLIBS $END_GROUP 127 | strip objdir/* 128 | 129 | rm -f arduino-preprocessor-${OUTPUT_VERSION}-${OUTPUT_TAG}.tar.bz2 130 | mv objdir arduino-preprocessor 131 | 132 | if [[ $OS == "Cygwin" ]] ; then 133 | cp cygwin-prebuilt/* arduino-preprocessor 134 | fi 135 | 136 | tar -cjvf arduino-preprocessor-${OUTPUT_VERSION}-${OUTPUT_TAG}.tar.bz2 arduino-preprocessor 137 | 138 | -------------------------------------------------------------------------------- /testsuite/.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | -------------------------------------------------------------------------------- /testsuite/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # This file is part of arduino-preprocessor. 5 | # 6 | # Copyright 2017 ARDUINO AG 7 | # 8 | # arduino-preprocessor is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | # 22 | # As a special exception, you may use this file as part of a free software 23 | # library without restriction. Specifically, if other files instantiate 24 | # templates or use macros or inline functions from this file, or you compile 25 | # this file and link it with other files to produce an executable, this 26 | # file does not by itself cause the resulting executable to be covered by 27 | # the GNU General Public License. This exception does not however 28 | # invalidate any other reasons why the executable file might be covered by 29 | # the GNU General Public License. 30 | # 31 | 32 | source term.sh 33 | 34 | test_preprocessor() { 35 | rm -rf tmp 36 | mkdir tmp 37 | 38 | TEST=$1 39 | say "@cyan[[Testing preprocessor on @b$TEST]]" 40 | ../arduino-preprocessor/arduino-preprocessor $TEST -- -std=gnu++11 > tmp/preproc.cpp 41 | if [ $? -ne 0 ]; then 42 | fail "Error running arduino-preprocessor" 43 | return 1 44 | fi 45 | 46 | say "@cyan[[Running compiler...]]" 47 | g++ -std=gnu++11 -c tmp/preproc.cpp -o tmp/preproc.o 48 | if [ $? -ne 0 ]; then 49 | # if the test fails output the preprocessed source code 50 | echo "" 51 | say "@cyan[[Preprocessor output with debugging enabled:]]" 52 | echo "" 53 | 54 | ../arduino-preprocessor/arduino-preprocessor -debug $TEST -- -std=gnu++11 55 | 56 | fail $TEST 57 | return 1 58 | fi 59 | pass $TEST 60 | return 0 61 | } 62 | 63 | # Try to build all sources in testdata 64 | hr 65 | FAILS=0 66 | TOTAL=0 67 | for TEST in `find testdata -name "test_*.cpp"`; do 68 | test_preprocessor $TEST 69 | FAILS=$(($FAILS+$?)) 70 | TOTAL=$(($TOTAL+1)) 71 | hr 72 | done 73 | 74 | echo $TOTAL tests run 75 | echo $FAILS tests failed 76 | 77 | exit $FAILS 78 | 79 | -------------------------------------------------------------------------------- /testsuite/term.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # This file is part of arduino-preprocessor. 5 | # 6 | # Copyright 2017 ARDUINO AG 7 | # 8 | # arduino-preprocessor is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation; either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program; if not, write to the Free Software 20 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | # 22 | # As a special exception, you may use this file as part of a free software 23 | # library without restriction. Specifically, if other files instantiate 24 | # templates or use macros or inline functions from this file, or you compile 25 | # this file and link it with other files to produce an executable, this 26 | # file does not by itself cause the resulting executable to be covered by 27 | # the GNU General Public License. This exception does not however 28 | # invalidate any other reasons why the executable file might be covered by 29 | # the GNU General Public License. 30 | # 31 | 32 | # Borrowed from https://stackoverflow.com/a/46331700 33 | say() { 34 | echo "$@" | sed \ 35 | -e "s/\(\(@\(red\|green\|yellow\|blue\|magenta\|cyan\|white\|reset\|b\|u\)\)\+\)[[]\{2\}\(.*\)[]]\{2\}/\1\4@reset/g" \ 36 | -e "s/@red/$(tput setaf 1)/g" \ 37 | -e "s/@green/$(tput setaf 2)/g" \ 38 | -e "s/@yellow/$(tput setaf 3)/g" \ 39 | -e "s/@blue/$(tput setaf 4)/g" \ 40 | -e "s/@magenta/$(tput setaf 5)/g" \ 41 | -e "s/@cyan/$(tput setaf 6)/g" \ 42 | -e "s/@white/$(tput setaf 7)/g" \ 43 | -e "s/@reset/$(tput sgr0)/g" \ 44 | -e "s/@b/$(tput bold)/g" \ 45 | -e "s/@u/$(tput sgr 0 1)/g" 46 | } 47 | 48 | hr() { 49 | echo "" 50 | echo "===========================================================================" 51 | echo "" 52 | } 53 | 54 | fail() { 55 | say "@b@red[[FAIL: $1]]" 56 | } 57 | 58 | pass() { 59 | say "@b@green[[PASS: $1]]" 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /testsuite/testdata/test_blink_avr.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino/arduino-preprocessor/c29e25ed459cddb9b9a0eec5d77395d382b21925/testsuite/testdata/test_blink_avr.cpp -------------------------------------------------------------------------------- /testsuite/testdata/test_issue_7.cpp: -------------------------------------------------------------------------------- 1 | // https://github.com/arduino/arduino-preprocessor/issues/7 2 | 3 | #line 1 "issue_7.ino" 4 | 5 | int a; int b=f(); 6 | 7 | int f() { 8 | return 1; 9 | } 10 | -------------------------------------------------------------------------------- /utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of arduino-preprocessor. 3 | * 4 | * Copyright 2017 BCMI LABS SA 5 | * 6 | * arduino-preprocessor is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | * As a special exception, you may use this file as part of a free software 21 | * library without restriction. Specifically, if other files instantiate 22 | * templates or use macros or inline functions from this file, or you compile 23 | * this file and link it with other files to produce an executable, this 24 | * file does not by itself cause the resulting executable to be covered by 25 | * the GNU General Public License. This exception does not however 26 | * invalidate any other reasons why the executable file might be covered by 27 | * the GNU General Public License. 28 | */ 29 | 30 | #pragma once 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | 38 | inline vector split(const string &in, const char sep) { 39 | vector res; 40 | istringstream params(in); 41 | string s; 42 | while (getline(params, s, sep)) { 43 | res.push_back(s); 44 | } 45 | return res; 46 | } 47 | 48 | inline bool stringToInt(const string &in, int *out) { 49 | stringstream ss(in); 50 | ss >> *out; 51 | if (!ss.fail()) { 52 | return true; 53 | } 54 | // Tolerate trailing white space 55 | ss >> ws; 56 | if (ss.eof()) { 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | inline bool startsWith(const string &in, const string &prefix) { 63 | int l = prefix.length(); 64 | return in.find(prefix.c_str(), 0, l) == 0; 65 | } 66 | 67 | inline bool cStrEndsWith(const char *str, const char *suffix) { 68 | if (str == NULL || suffix == NULL) 69 | return false; 70 | 71 | size_t strLen = strlen(str); 72 | size_t suffixLen = strlen(suffix); 73 | 74 | if (suffixLen > strLen) 75 | return false; 76 | 77 | return strncmp(str + strLen - suffixLen, suffix, suffixLen) == 0; 78 | } 79 | 80 | #ifdef WIN32 81 | 82 | std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) { 83 | size_t start_pos = 0; 84 | while((start_pos = str.find(from, start_pos)) != std::string::npos) { 85 | str.replace(start_pos, from.length(), to); 86 | start_pos += to.length(); // Handles case where 'to' is a substring of 'from' 87 | } 88 | return str; 89 | } 90 | 91 | inline std::string quoteCppString(std::string& str) { 92 | str = ReplaceAll(str, "\\", "\\\\"); 93 | return str; 94 | } 95 | #else 96 | inline std::string quoteCppString(std::string& str) { 97 | return str; 98 | } 99 | #endif 100 | --------------------------------------------------------------------------------