andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 1 | # Clang Tool Refactoring |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 2 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 3 | [TOC] |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 4 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 5 | ## Caveats |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 6 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 7 | * The current workflow requires git. |
| 8 | * This doesn't work on Windows... yet. I'm hoping to have a proof-of-concept |
| 9 | working on Windows as well ~~in a month~~ several centuries from now. |
| 10 | |
| 11 | ## Prerequisites |
| 12 | |
| 13 | Everything needed should be in a default Chromium checkout using gclient. |
| 14 | `third_party/llvm-build/Release+Asserts/bin` should be in your `$PATH`. |
| 15 | |
| 16 | ## Writing the Tool |
| 17 | |
| 18 | An example clang tool is being implemented in |
| 19 | https://codereview.chromium.org/12746010/. Other useful resources might be the |
| 20 | [basic tutorial for Clang's AST matchers](http://clang.llvm.org/docs/LibASTMatchersTutorial.html) |
| 21 | or the |
| 22 | [AST matcher reference](http://clang.llvm.org/docs/LibASTMatchersReference.html). |
| 23 | |
| 24 | Build your tool by running the following command (requires cmake version 2.8.10 |
| 25 | or later): |
| 26 | |
| 27 | ```shell |
Nico Weber | e250e6a | 2015-12-02 19:31:56 | [diff] [blame^] | 28 | tools/clang/scripts/update.py --force-local-build --without-android \ |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 29 | --with-chrome-tools <tools> |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 30 | ``` |
| 31 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 32 | `<tools>` is a semicolon delimited list of subdirectories in `tools/clang` to |
| 33 | build. The resulting binary will end up in |
| 34 | `third_party/llvm-build/Release+Asserts/bin`. For example, to build the Chrome |
| 35 | plugin and the empty\_string tool, run the following: |
| 36 | |
| 37 | ```shell |
Nico Weber | e250e6a | 2015-12-02 19:31:56 | [diff] [blame^] | 38 | tools/clang/scripts/update.py --force-local-build --without-android \ |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 39 | --with-chrome-tools "plugins;empty_string" |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 40 | ``` |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 41 | |
| 42 | When writing AST matchers, the following can be helpful to see what clang thinks |
| 43 | the AST is: |
| 44 | |
| 45 | ```shell |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 46 | clang++ -cc1 -ast-dump foo.cc |
| 47 | ``` |
| 48 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 49 | ## Running the tool |
| 50 | |
| 51 | First, you'll need to generate the compilation database with the following |
| 52 | command: |
| 53 | |
| 54 | ```shell |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 55 | cd $HOME/src/chrome/src |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 56 | ninja -C out/Debug -t compdb cc cxx objc objcxx > \ |
| 57 | out/Debug/compile_commands.json |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 58 | ``` |
| 59 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 60 | This will dump the command lines used to build the C/C++ modules in all of |
| 61 | Chromium into the resulting file. Then run the following command to run your |
| 62 | tool across all Chromium code: |
| 63 | |
| 64 | ```shell |
| 65 | # Make sure all chromium targets are built to avoid missing generated |
| 66 | # dependencies |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 67 | ninja -C out/Debug |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 68 | tools/clang/scripts/run_tool.py <toolname> \ |
| 69 | <path/to/directory/with/compile_commands.json> <path 1> <path 2> ... |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 70 | ``` |
| 71 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 72 | `<path 1>`, `<path 2>`, etc are optional arguments you use to filter the files |
| 73 | that will be rewritten. For example, if you only want to run the `empty-string` |
| 74 | tool on files in `chrome/browser/extensions` and `sync`, you'd do something like: |
| 75 | |
| 76 | ```shell |
| 77 | tools/clang/scripts/run_tool.py empty_string out/Debug \ |
| 78 | chrome/browser/extensions sync |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 79 | ``` |
| 80 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 81 | ## Limitations |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 82 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 83 | Since the compile database is generated by ninja, that means that files that |
| 84 | aren't compiled on that platform won't be processed. That means if you want to |
| 85 | apply a change across all Chromium platforms, you'll have to run the tool once |
| 86 | on each platform. |
| 87 | |
| 88 | ## Testing |
| 89 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 90 | `test_tool.py` is the test harness for running tests. To use it, simply run: |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 91 | |
| 92 | ```shell |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 93 | test_tool.py <tool name> |
| 94 | ``` |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 95 | |
| 96 | Note that name of the built tool and the subdirectory it lives in at |
| 97 | `tools/clang` must match. What the test harness does is find all files that |
| 98 | match the pattern `*-original.cc` in your tool's tests subdirectory. It then |
| 99 | runs the tool across those files and compares it to the expected result, stored |
| 100 | in `*-expected.cc` |