├── .eslintrc.json ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RELEASING.md ├── azure-pipelines.yml ├── code-of-conduct.md ├── images ├── document-links.png ├── fuchsia-logo-256x256.png ├── fxrev-and-fxbug-links.png ├── references.png └── terminal-links.png ├── package-lock.json ├── package.json ├── src ├── extension.ts ├── fuchsia_paths.ts ├── log.ts ├── provider.ts └── test │ ├── runTest.ts │ └── suite │ ├── extension.test.ts │ └── index.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/naming-convention": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode-test/ 5 | *.vsix 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "${defaultBuildTask}" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--extensionDevelopmentPath=${workspaceFolder}", 26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 27 | ], 28 | "outFiles": [ 29 | "${workspaceFolder}/out/test/**/*.js" 30 | ], 31 | "preLaunchTask": "${defaultBuildTask}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | .yarnrc 7 | vsc-extension-quickstart.md 8 | **/tsconfig.json 9 | **/.eslintrc.json 10 | **/*.map 11 | **/*.ts 12 | !file.ts 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "fuchsiaware" extension will be documented in this file. 4 | 5 | ## [0.0.1] 6 | 7 | - Initial release: Document Links 8 | * Links Fuchsia component URLs (beginning with `fuchsia-pkg://fuchsia.com/...`) in a document text 9 | to component manifests (files with extensions .cml or .cmx). 10 | 11 | ## [0.0.2] 12 | 13 | - New feature: References 14 | * Provides references to a given manifest, via matching component URLs in other files. 15 | 16 | ## [0.0.3] 17 | 18 | - New feature: Settings 19 | * Provides settings to override the default location for the `fuchsia` source tree, and now also 20 | now uses an environment variable (`$FUCHSIA_DIR` or `$FUCHSIA_ROOT`, if either is set) as a 21 | fallback, if the current workspace does not include a folder rooted at the `fuchsia` directory. 22 | - Fixes: 23 | * The data extraction patterns have improved, enabling a significant number of additional package 24 | links. 25 | 26 | ## [0.0.4] 27 | 28 | - New feature: Terminal Links 29 | * Links Fuchsia component URLs (beginning with `fuchsia-pkg://fuchsia.com/...`) in the terminal 30 | to component manifests (files with extensions .cml or .cmx). 31 | 32 | ## [0.0.5] 33 | 34 | - Fixes: 35 | * Added more patterns and heuristics to resolve more component URLs to their manifests. 36 | 37 | ## [0.1.0] 38 | 39 | - Initial public release, published to the Visual Studio Marketplace. 40 | 41 | ## [0.2.0] 42 | 43 | - New feature: Linkifies fxrev.dev and fxbug.dev links 44 | * Activates `fxrev.dev/[revision ID]` (and legacy `fxr/[revision ID]`) links 45 | * Activates `fxbug.dev/[bug ID]` (and legacy `fxb/[bug ID]`) links 46 | 47 | ## [0.3.0] 48 | 49 | - Fixes: 50 | * Added more patterns and heuristics to resolve more component URLs to their manifests. 51 | (Heuristics can be turned off in "Settings", if desired.) This release applies a best effort 52 | match of otherwise unresolved component URL links to manifests with matching component names. 53 | Since the same component can be in more than one package, this will not always be valid, but 54 | for most users that would have to search manually, this seems to be more helpful. In addition, 55 | to searching based on matching names, matches are also attempted after removing some common 56 | built-time-appended suffixes (such as "_test"). 57 | 58 | ## [0.4.0] 59 | 60 | - Fixes: 61 | * A recent change to `fx set` appears to now populate `.fx-build-dir` with an absolute path name. 62 | This version supports an absolute path name, as long as it is a descendent directory of 63 | `$FUCHSIA_DIR`. This should resolve errors related to the recent change. If support for absolute 64 | paths external to `$FUCHSIA_DIR` is also necessary, additional changes will be required. 65 | 66 | ## [0.4.1] 67 | 68 | - Documentation: 69 | * Adds instructions for publishing new releases, in [RELEASING.md](RELEASING.md). 70 | 71 | ## [0.4.2] 72 | 73 | - UI: 74 | * Updates the VS Code extension icon to a more current Fuchsia logo. (Note: 75 | the version of the logo image in this release did not work well with dark 76 | themes, and had to be fixed in version 0.4.3.) 77 | 78 | - Test regression: 79 | * A test is no longer passing, even though the source has not changed. Until 80 | this issue can be corrected, I had to comment out the broken test. See issue 81 | (#13)[https://github.com/google/fuchsiaware/issues/13] for details. 82 | 83 | ## [0.4.2] 84 | 85 | - UI: 86 | * Replace the new VS Code extension icon with one that is more visible on top 87 | of dark themes in VS Code and web pages. 88 | 89 | - Documentation: 90 | * Improved steps in the [RELEASING][RELEASING.md] document to ensure the 91 | package.json version change is included in the pull request with its 92 | corresponding changes. 93 | 94 | ## [0.5.0] 95 | 96 | - Fixes: 97 | * Updated the regular expression for extracting packages, and updated tests to 98 | match the revised ninja build command to build packages. The commands for 99 | building packages recently changed, and the packages could not be extracted. 100 | This generated a VS Code error dialog when loading FuchsiAware, and 101 | FuchsiAware was not able to generate some cross-references. 102 | 103 | ## [0.5.1] 104 | 105 | - Fixes: 106 | * Adjust additional regular expressions to adapt to build rule changes, to 107 | fix extracted names and paths. This corrects missing and/or invalid 108 | document links from component URLs to CML manifest sources. 109 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code Reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Test Code Changes (Before Review) 26 | 27 | If you are contributing a code change, please run the existing tests. Strongly 28 | consider adding a test for your specific change. 29 | 30 | You can run the tests using the command: 31 | 32 | ```shell 33 | $ npm run test 34 | ``` 35 | 36 | Additional information on the testing framework and commands is available in the 37 | VS Code documentation for 38 | [Testing Extensions](https://code.visualstudio.com/api/working-with-extensions/testing-extension). 39 | 40 | ## Community Guidelines 41 | 42 | This project follows [Google's Open Source Community 43 | Guidelines](https://opensource.google/conduct/). 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FuchsiAware 2 | ## Fuchsia Source Tree Browsing Assistance 3 | 4 | [![vscode-marketplace](https://img.shields.io/vscode-marketplace/d/RichKadel.fuchsiaware.svg)](https://marketplace.visualstudio.com/items?itemName=RichKadel.fuchsiaware) 5 | [![license](https://img.shields.io/badge/license-Apache2.0-blue.svg)](https://github.com/google/fuchsiaware/blob/main/LICENSE) 6 | [![Build Status](https://dev.azure.com/vscode-fuchsiaware/FuchsiAware/_apis/build/status/google.fuchsiaware?branchName=main)](https://dev.azure.com/vscode-fuchsiaware/FuchsiAware/_build/latest?definitionId=1&branchName=main) 7 | 8 | This extension provides additional support to developers working with the source code for the 9 | open source [Fuchsia](https://fuchsia.dev) operating system. 10 | 11 | The extension recognizes Fuchsia-specific artifacts in the Fuchsia Git-based source repository 12 | (also known as `fuchsia.git`), and adds features to help navigate them, such as: 13 | 14 | * Links from fuchsia component URLs (beginning with the `fuchsia-pkg` scheme), in any file, to the 15 | component's "manifest" source file (either `.cml` or `.cmx`). 16 | 17 |    ![document-links](images/document-links.png) 18 | 19 | * Links from fuchsia component URLs (beginning with the `fuchsia-pkg` scheme) in terminal output. 20 | component's "manifest" source file (either `.cml` or `.cmx`). 21 | 22 |    ![terminal-links](images/terminal-links.png) 23 | 24 | * References back from the manifest to any known source with a `fuchsia-pkg` component URL to the 25 | manifest's component. Right-click anywhere in the manifest source (in the VS Code editor), and 26 | select one of the menu options "Go to References" or "Find References". 27 | 28 |    ![references](images/references.png) 29 | 30 | * Activates links to prefixed Fuchsia revision and bug IDs; for example: 31 | - `fxrev.dev/[revision ID]` (and legacy `fxr/[revision ID]`) link to `https://fxrev.dev/[revision ID]` 32 | - `fxbug.dev/[bug ID]` (and legacy `fxb/[bug ID]`) link to `https://fxbug.dev/[revision ID]` 33 | 34 |    ![fxrev-and-fxbug-links](images/fxrev-and-fxbug-links.png) 35 | 36 | ## Minimum Requirements and Implementation Details 37 | 38 | * You can run the `git` command. 39 | * Your VS Code workspace has exactly one workspace folder that is the root of your cloned `fuchsia` 40 | repo. (This can be overridden in VS Code "Settings".) 41 | * You ran `fx set ...` (or `fx use ...`) to set the current build directory, and generate _Ninja_ 42 | build dependencies. 43 | 44 | The extension reads build commands from the `toolchain.ninja` file in your build directory to 45 | determine the package and component names assocated with each manifest (to establish links from 46 | matching component URLs), and uses the `git grep` command to find references from any text document 47 | in the `fuchsia.git` repo to a known component URL. 48 | 49 | Note that a component URL is linked only if its manifest is included in the current `fx set ...` 50 | build configuration. 51 | 52 | ## Release Notes 53 | 54 | See the [CHANGELOG](CHANGELOG.md) 55 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Test and Release Process 2 | 3 | This is an informal document to capture the steps to prepare and release a new 4 | patch or release of FuchsiAware. 5 | 6 | ## Contributing a Change 7 | 8 | A code change (bug fix or new feature) should be submitted under its own Pull 9 | Request, _without_ changing the version in `package.json`. (This way, multiple 10 | code changes can be bundled into a new release, if desired.) See the 11 | [CONTRIBUTING](CONTRIBUTING.md) page. 12 | 13 | Most changes should be accompanied by a brief [CHANGELOG](CHANGELOG.md) entry. 14 | If you are contributing a change but will not be publishing the change (which 15 | requires admin rights for the Visual Studio Marketplace), add a bullet item at 16 | the bottom of the CHANGELOG, under the heading "`## [NEXT]`". (Add this heading, 17 | if not already present.) 18 | 19 | ## Preparing a New Release 20 | 21 | 1. Determine the new version ID (**_but do not update `package.json` yet!_**) by 22 | deciding if the release is only a patch, a minor release, or a major release. 23 | For example, if the current release is `0.2.1`: 24 | 25 | * A patch will result in a new version ID `0.2.2`. A patch is typically used 26 | if there is no code change to the extension itself (perhaps only changing 27 | or adding a test, or changing documentation); or the patch results in a 28 | minor, optional behavior change. 29 | * A minor release will result in a new version ID `0.3.0` (note the patch ID 30 | is reset to `0`). A minor release typically represents a significant code 31 | change, change in behavior, a fix to a bug that inhibits end-user features, 32 | or a new feature. 33 | * A major release is rare, and typically involves a high bar of release 34 | criteria and feedback (if not approval) from major stakeholders. 35 | 36 | 2. Fetch all recently merged changes, and start a new branch, named for the new 37 | version ID. From the toplevel of your cloned, forked repo: 38 | 39 | ```shell 40 | # One-time setup: 41 | # $ git remote add upstream git@github.com:google/fuchsiaware.git 42 | $ git fetch upstream main 43 | $ NEW_VERSION="0.2.2" # or whatever the new version will be 44 | $ git checkout -b $NEW_VERSION upstream/main 45 | ``` 46 | 47 | 3. Confirm the tests still pass. (If not, start a new branch, push, and merge a 48 | new pull request with a fix.) 49 | 50 | ```shell 51 | $ npm run test 52 | ... 53 | ``` 54 | 55 | 4. Add the new version ID and a brief description of each change to the 56 | [CHANGELOG](CHANGELOG.md). Then push the change to your fork. 57 | 58 | ```shell 59 | $ git add ... 60 | $ git commit [--amend] 61 | $ git push --set-upstream origin $NEW_VERSION 62 | ``` 63 | 64 | Create a pull request, and verify the GitHub checks pass. 65 | 66 | DO NOT merge the change yet. 67 | 68 | _Your new version is **not yet published** to the VS Code Marketplace_ 69 | 70 | 5. Use `vsce` to generate the `.vsix` file for the new version, and then publish 71 | the new version, with the new version ID. 72 | 73 | ```shell 74 | $ vsce package 75 | $ vsce publish --githubBranch=main 76 | ``` 77 | 78 | This should update the version in `package.json` and `package-lock.json`, and 79 | automatically generate a new local commit in your branch. 80 | 81 | Additional information on the publishing commands, including how to install 82 | vsce and/or login as the required "publisher" ("RichKadel"), if necessary, is 83 | available in the VS Code documentation for 84 | [Publishing Extensions](https://code.visualstudio.com/api/working-with-extensions/publishing-extension). 85 | 86 | 6. Push the package version changes made by `vsce publish` to your pull request 87 | and verify the pull request in GitHub includes the additional commit, and 88 | the `package.json` file (and lock file) have the correct version. 89 | 90 | ```shell 91 | $ git push --force-with-lease 92 | ``` 93 | 94 | Once the GitHub checks pass, merge the pull request. 95 | 96 | 7. Tag the release (via the GitHub UI, apply a new Release/Tag named using the 97 | new version ID) 98 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | branches: 3 | include: 4 | - master 5 | tags: 6 | include: 7 | - v* 8 | 9 | strategy: 10 | matrix: 11 | linux: 12 | imageName: 'ubuntu-latest' 13 | # mac: 14 | # imageName: 'macos-latest' 15 | # windows: 16 | # imageName: 'vs2017-latest' 17 | 18 | pool: 19 | vmImage: $(imageName) 20 | 21 | steps: 22 | 23 | - task: NodeTool@0 24 | inputs: 25 | versionSpec: '10.x' 26 | displayName: 'Install Node.js' 27 | 28 | - bash: | 29 | /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 30 | echo ">>> Started xvfb" 31 | displayName: Start xvfb 32 | condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) 33 | 34 | - bash: | 35 | echo ">>> Compile vscode-test" 36 | yarn && yarn compile 37 | echo ">>> Compiled vscode-test" 38 | cd sample 39 | echo ">>> Run sample integration test" 40 | yarn && yarn compile && yarn test 41 | displayName: Run Tests 42 | env: 43 | DISPLAY: ':99.0' 44 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when the Project 56 | Steward has a reasonable belief that an individual's behavior may have a 57 | negative impact on the project or its community. 58 | 59 | ## Conflict Resolution 60 | 61 | We do not believe that all conflict is bad; healthy debate and disagreement 62 | often yield positive results. However, it is never okay to be disrespectful or 63 | to engage in behavior that violates the project’s code of conduct. 64 | 65 | If you see someone violating the code of conduct, you are encouraged to address 66 | the behavior directly with those involved. Many issues can be resolved quickly 67 | and easily, and this gives people more control over the outcome of their 68 | dispute. If you are unable to resolve the matter for any reason, or if the 69 | behavior is threatening or harassing, report it. We are dedicated to providing 70 | an environment where participants feel welcome and safe. 71 | 72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the 73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to 74 | receive and address reported violations of the code of conduct. They will then 75 | work with a committee consisting of representatives from the Open Source 76 | Programs Office and the Google Open Source Strategy team. If for any reason you 77 | are uncomfortable reaching out to the Project Steward, please email 78 | opensource@google.com. 79 | 80 | We will investigate every complaint, but you may not receive a direct response. 81 | We will use our discretion in determining when and how to follow up on reported 82 | incidents, which may range from not taking action to permanent expulsion from 83 | the project and project-sponsored spaces. We will notify the accused of the 84 | report and provide them an opportunity to discuss it before any action is taken. 85 | The identity of the reporter will be omitted from the details of the report 86 | supplied to the accused. In potentially harmful situations, such as ongoing 87 | harassment or threats to anyone's safety, we may take action without notice. 88 | 89 | ## Attribution 90 | 91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 92 | available at 93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 94 | -------------------------------------------------------------------------------- /images/document-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fuchsiaware/1c2833c26379a0b9a072aa8101eb21718c738128/images/document-links.png -------------------------------------------------------------------------------- /images/fuchsia-logo-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fuchsiaware/1c2833c26379a0b9a072aa8101eb21718c738128/images/fuchsia-logo-256x256.png -------------------------------------------------------------------------------- /images/fxrev-and-fxbug-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fuchsiaware/1c2833c26379a0b9a072aa8101eb21718c738128/images/fxrev-and-fxbug-links.png -------------------------------------------------------------------------------- /images/references.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fuchsiaware/1c2833c26379a0b9a072aa8101eb21718c738128/images/references.png -------------------------------------------------------------------------------- /images/terminal-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fuchsiaware/1c2833c26379a0b9a072aa8101eb21718c738128/images/terminal-links.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fuchsiaware", 3 | "displayName": "FuchsiAware", 4 | "description": "Assist browsing Fuchsia artifacts, such as by linking from component URLs to component manifests.", 5 | "version": "0.5.1", 6 | "publisher": "RichKadel", 7 | "license": "Apache-2.0", 8 | "engines": { 9 | "vscode": "^1.48.0" 10 | }, 11 | "categories": [ 12 | "Other" 13 | ], 14 | "repository": { 15 | "url": "https://github.com/google/fuchsiaware" 16 | }, 17 | "activationEvents": [ 18 | "onStartupFinished" 19 | ], 20 | "main": "./out/extension.js", 21 | "icon": "images/fuchsia-logo-256x256.png", 22 | "contributes": { 23 | "configuration": { 24 | "title": "FuchsiAware", 25 | "properties": { 26 | "fuchsiAware.fuchsia.rootDirectory": { 27 | "scope": "window", 28 | "type": "string", 29 | "default": "", 30 | "description": "Path to the root directory of the fuchsia.git source code. (Defaults to the workspace folder rooted at a fuchsia.git clone, or environment variable $FUCHSIA_DIR or $FUCHSIA_ROOT.)" 31 | }, 32 | "fuchsiAware.showUnresolvedTerminalLinks": { 33 | "scope": "window", 34 | "type": "boolean", 35 | "default": false, 36 | "description": "Highlights every 'fuchsia.pkg://...' component URL, even if the associated component manifest is unknown. (The link tooltip will denote resolved versus unknown manifests, before clicking.)" 37 | }, 38 | "fuchsiAware.normalizeWordSeparators": { 39 | "scope": "window", 40 | "type": "boolean", 41 | "default": true, 42 | "description": "Ignores differences between dashes and underscores comparing URLs. This can help resolve additional links to manifests, but in rare cases may link a component URL to the wrong manifest." 43 | }, 44 | "fuchsiAware.useHeuristicsToFindMoreLinks": { 45 | "scope": "window", 46 | "type": "boolean", 47 | "default": true, 48 | "description": "Applies additional patterns to match component URLs to manifests that are based on common patterns, but not easily validated. This can help resolve additional links, but may link a component URL to the wrong manifest." 49 | }, 50 | "fuchsiAware.debug": { 51 | "scope": "window", 52 | "type": "boolean", 53 | "default": false, 54 | "description": "Enable logging of additional debugging messages" 55 | } 56 | } 57 | }, 58 | "languages": [ 59 | { 60 | "id": "untitled-fuchsia-manifest", 61 | "extensions": [ 62 | ".untitled_fuchsia_manifest" 63 | ] 64 | }, 65 | { 66 | "id": "json", 67 | "extensions": [ 68 | ".cmx" 69 | ] 70 | }, 71 | { 72 | "id": "json5", 73 | "extensions": [ 74 | ".cml" 75 | ] 76 | } 77 | ] 78 | }, 79 | "scripts": { 80 | "vscode:prepublish": "npm run compile", 81 | "compile": "tsc -p ./", 82 | "watch": "tsc -watch -p ./", 83 | "pretest": "npm run compile && npm run lint", 84 | "lint": "eslint src --ext ts", 85 | "test": "node ./out/test/runTest.js" 86 | }, 87 | "devDependencies": { 88 | "@types/glob": "^7.1.3", 89 | "@types/mocha": "^8.0.4", 90 | "@types/node": "^12.11.7", 91 | "@types/vscode": "^1.48.0", 92 | "@typescript-eslint/eslint-plugin": "^4.9.0", 93 | "@typescript-eslint/parser": "^4.9.0", 94 | "eslint": "^7.15.0", 95 | "glob": "^7.1.6", 96 | "mocha": "^8.1.3", 97 | "typescript": "^4.7.4", 98 | "vscode-test": "^1.4.1" 99 | }, 100 | "extensionDependencies": [ 101 | "mrmlnc.vscode-json5" 102 | ], 103 | "__metadata": { 104 | "id": "8bf964d9-8f48-43d9-9ff4-6c4fc4800306", 105 | "publisherDisplayName": "Rich Kadel", 106 | "publisherId": "d8353dbb-970b-403e-a42c-0b6af1a4ac1e", 107 | "isPreReleaseVersion": false 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as vscode from 'vscode'; 16 | import * as log from './log'; 17 | import { findFuchsiaBuildDir } from './fuchsia_paths'; 18 | import { Provider } from './provider'; 19 | 20 | // this method is called when vs code is activated 21 | export function activate(context: vscode.ExtensionContext) { 22 | _asyncActivate(context).then((success) => log.info( 23 | `The FuchsiAware extension initialization completed ${success ? 'successfully' : 'with errors'}` 24 | )); 25 | } 26 | 27 | async function _asyncActivate(context: vscode.ExtensionContext): Promise { 28 | log.info('The FuchsiAware extension is activated and looking for the fuchsia source...'); 29 | 30 | const result = await findFuchsiaBuildDir(); 31 | if (!result) { 32 | return false; 33 | } 34 | const [baseUri, buildDir] = result; 35 | 36 | const provider = new Provider(baseUri, buildDir); 37 | 38 | if (!await provider.init()) { 39 | return false; 40 | } 41 | 42 | context.subscriptions.push(vscode.Disposable.from( 43 | vscode.languages.registerDocumentLinkProvider({ scheme: Provider.scheme }, provider), 44 | )); 45 | log.info('The DocumentLinkProvider is initialized and registered.'); 46 | context.subscriptions.push(vscode.Disposable.from( 47 | vscode.window.registerTerminalLinkProvider(provider), 48 | )); 49 | log.info('The TerminalLinkProvider is initialized and registered.'); 50 | context.subscriptions.push(vscode.Disposable.from( 51 | vscode.languages.registerReferenceProvider({ scheme: Provider.scheme }, provider), 52 | )); 53 | log.info('The ReferenceProvider is initialized and registered.'); 54 | return true; 55 | } 56 | -------------------------------------------------------------------------------- /src/fuchsia_paths.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as vscode from 'vscode'; 16 | import * as log from './log'; 17 | 18 | const FUCHSIA_DIR_SETTING_KEY = 'fuchsiAware.fuchsia.rootDirectory'; 19 | 20 | export async function findFuchsiaBuildDir(): Promise<[vscode.Uri, string] | undefined> { 21 | const buildDirFilename = '.fx-build-dir'; 22 | if (!vscode.workspace.workspaceFolders) { 23 | return; 24 | } 25 | let useFuchsiaDirFromEnv = false; 26 | let fuchsiaDirFromEnv: string | undefined = 27 | vscode.workspace.getConfiguration().get(FUCHSIA_DIR_SETTING_KEY); 28 | let fuchsiaDirVariable; 29 | if (fuchsiaDirFromEnv) { 30 | fuchsiaDirVariable = `VS Code setting '${FUCHSIA_DIR_SETTING_KEY}'`; 31 | useFuchsiaDirFromEnv = true; 32 | } else { 33 | fuchsiaDirFromEnv = process.env.FUCHSIA_DIR; 34 | if (fuchsiaDirFromEnv) { 35 | fuchsiaDirVariable = `environment variable '$FUCHSIA_DIR'`; 36 | } else { 37 | fuchsiaDirFromEnv = process.env.FUCHSIA_ROOT; 38 | if (fuchsiaDirFromEnv) { 39 | fuchsiaDirVariable = `environment variable '$FUCHSIA_ROOT'`; 40 | } 41 | } 42 | } 43 | let fuchsiaSubdirUri; 44 | for (const folder of vscode.workspace.workspaceFolders) { 45 | let buildDir; 46 | if (!useFuchsiaDirFromEnv || folder.uri.path === fuchsiaDirFromEnv) { 47 | buildDir = await _readBuildDirDoc(folder.uri, buildDirFilename); 48 | } 49 | if (buildDir) { 50 | return [folder.uri, buildDir]; 51 | } else if (!fuchsiaSubdirUri && fuchsiaDirFromEnv && 52 | folder.uri.fsPath.startsWith(fuchsiaDirFromEnv)) { 53 | fuchsiaSubdirUri = folder.uri; 54 | } 55 | } 56 | if (!useFuchsiaDirFromEnv) { 57 | log.info( 58 | `Could not find file '${buildDirFilename}' in the root of any workspace folder: \n ` + 59 | vscode.workspace.workspaceFolders.map((folder) => folder.uri).join('\n ') 60 | ); 61 | } 62 | if (fuchsiaDirFromEnv) { 63 | if (fuchsiaSubdirUri) { 64 | const fuchsiaDirUri = fuchsiaSubdirUri.with({ path: fuchsiaDirFromEnv }); 65 | const buildDir = await _readBuildDirDoc(fuchsiaDirUri, buildDirFilename); 66 | if (buildDir) { 67 | log.info(`Loading provider data from ${fuchsiaDirVariable} = '${fuchsiaDirUri}'`); 68 | return [fuchsiaDirUri, buildDir]; 69 | } else { 70 | log.info( 71 | `nor was it found in the directory from ${fuchsiaDirVariable} (${fuchsiaDirUri}).` 72 | ); 73 | } 74 | } else { 75 | log.info( 76 | `nor was any workspace folder found under the directory from ${fuchsiaDirVariable} ` + 77 | `(${fuchsiaDirFromEnv}).` 78 | ); 79 | } 80 | } else { 81 | log.info( 82 | `and the fuchsia root directory could not be determined from either ` + 83 | `settings ('${FUCHSIA_DIR_SETTING_KEY}') or an environment variable ($FUCHSIA_DIR or ` + 84 | `$FUCHSIA_ROOT.` 85 | ); 86 | } 87 | log.info( 88 | `If you have an open workspace for the 'fuchsia.git' source tree, make sure the ` + 89 | `workspace is rooted at the repository root directory (e.g., 'fuchsia'), and ` + 90 | 'run `fx set ...` or `fx use ...`, then reload the VS Code window.' 91 | ); 92 | return; 93 | } 94 | 95 | async function _readBuildDirDoc( 96 | folderUri: vscode.Uri, 97 | buildDirFilename: string 98 | ): Promise { 99 | const buildDirFileUri = folderUri.with({ path: `${folderUri.path}/${buildDirFilename}` }); 100 | try { 101 | const buildDirDoc = await vscode.workspace.openTextDocument(buildDirFileUri); 102 | let buildDir = buildDirDoc.getText().trim(); 103 | // buildDir can be an absolute path; make it relative to folderUri.fsPath. 104 | if (buildDir.startsWith(`${folderUri.fsPath}/`)) { 105 | 106 | buildDir = buildDir.substring(folderUri.fsPath.length + 1); 107 | } 108 | log.info( 109 | `Folder '${folderUri.fsPath}' contains the '${buildDirFilename}' file, ` + 110 | `which specifies that the current Fuchsia build directory is '${buildDir}'` 111 | ); 112 | return buildDir; 113 | } catch (err) { 114 | return; 115 | } // the file probably doesn't exist in this folder 116 | } 117 | -------------------------------------------------------------------------------- /src/log.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as vscode from 'vscode'; 16 | 17 | export const DEBUG = vscode.workspace.getConfiguration().get('fuchsiAware.debug') ?? false; 18 | 19 | const fuchsiawareOutput = vscode.window.createOutputChannel('FuchsiAware'); 20 | 21 | export function debug(message: string) { 22 | if (DEBUG) { 23 | console.log(`DEBUG (fuchsiaware): ${message}`); 24 | fuchsiawareOutput.appendLine(`DEBUG: ${message}`); 25 | } 26 | } 27 | 28 | export function info(message: string) { 29 | console.log(`INFO (fuchsiaware): ${message}`); 30 | fuchsiawareOutput.appendLine(`INFO: ${message}`); 31 | } 32 | 33 | export function warn(message: string) { 34 | console.log(`WARNING (fuchsiaware): ${message}`); 35 | fuchsiawareOutput.appendLine(`WARNING: ${message}`); 36 | vscode.window.showWarningMessage(message); 37 | } 38 | 39 | export function error(message: string) { 40 | console.log(`ERROR (fuchsiaware): ${message}`); 41 | fuchsiawareOutput.appendLine(`ERROR: ${message}`); 42 | vscode.window.showErrorMessage(message); 43 | } 44 | -------------------------------------------------------------------------------- /src/provider.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as vscode from 'vscode'; 16 | import * as child_process from 'child_process'; 17 | import * as fs from 'fs'; 18 | import * as readline from 'readline'; 19 | import * as log from './log'; 20 | 21 | const SHOW_UNRESOLVED_TERMINAL_LINKS = vscode.workspace.getConfiguration().get( 22 | 'fuchsiAware.showUnresolvedTerminalLinks' 23 | ) ?? false; 24 | const NORMALIZE_WORD_SEPARATORS = vscode.workspace.getConfiguration().get( 25 | 'fuchsiAware.normalizeWordSeparators' 26 | ) ?? true; 27 | const USE_HEURISTICS_TO_FIND_MORE_LINKS = vscode.workspace.getConfiguration().get( 28 | 'fuchsiAware.useHeuristicsToFindMoreLinks' 29 | ) ?? true; 30 | 31 | interface FuchsiAwareLink extends vscode.TerminalLink { 32 | uri?: vscode.Uri; 33 | } 34 | 35 | export class Provider implements 36 | vscode.DocumentLinkProvider, 37 | vscode.TerminalLinkProvider, 38 | vscode.ReferenceProvider { 39 | 40 | static scheme = '*'; 41 | 42 | private _baseUri: vscode.Uri; 43 | private _buildDir: string; 44 | private _manifestFileNameToPackageAndComponents = new Map(); 45 | private _componentToSomeManifestPath = new Map(); 46 | private _packageAndComponentToManifestUri = new Map(); 47 | private _sourcePathToPackageAndComponents = new Map(); 48 | private _packageAndComponentToReferences = new Map(); 49 | private _nonNormalizedLinksResolved = 0; 50 | 51 | constructor(baseUri: vscode.Uri, buildDir: string) { 52 | this._baseUri = baseUri; 53 | this._buildDir = buildDir; 54 | } 55 | 56 | dispose() { 57 | this._manifestFileNameToPackageAndComponents.clear(); 58 | this._componentToSomeManifestPath.clear(); 59 | this._packageAndComponentToManifestUri.clear(); 60 | this._sourcePathToPackageAndComponents.clear(); 61 | this._packageAndComponentToReferences.clear(); 62 | } 63 | 64 | async init(): Promise { 65 | const [gotLinks, gotReferences] = await Promise.all([ 66 | this._getLinksToManifests(), 67 | this._getReferencesToManifests() 68 | ]); 69 | 70 | if (!(gotLinks && gotReferences)) { 71 | return false; 72 | } 73 | 74 | if (USE_HEURISTICS_TO_FIND_MORE_LINKS) { 75 | this._resolveUnmatchedUrlsToAnyMatchingManifestByName(); 76 | } 77 | 78 | if (log.DEBUG) { 79 | let totalReferences = 0; 80 | for (const references of this._packageAndComponentToReferences.values()) { 81 | totalReferences += references.length; 82 | } 83 | let totalUnresolvedLinks = 0; 84 | for (const [packageAndComponent, locations] of this._packageAndComponentToReferences) { 85 | if (!this._packageAndComponentToManifestUri.get(packageAndComponent)) { 86 | totalUnresolvedLinks++; 87 | const [ 88 | packageName, 89 | componentName, 90 | ] = packageAndComponent.split('/'); 91 | log.debug( 92 | `UNRESOLVED: fuchsia-pkg://fuchsia.com/${packageName}?#meta/${componentName}.cm(x)?` 93 | ); 94 | } 95 | } 96 | // TODO(#1): Clean up and expand on these reported stats 97 | log.debug(`Link Resolution Statistics`); 98 | log.debug(`==========================`); 99 | log.debug(`unresolved links = ${totalUnresolvedLinks}`); 100 | log.debug(`manifests = ${this._manifestFileNameToPackageAndComponents.size}`); 101 | log.debug(`references = ${totalReferences}`); 102 | log.debug(`nonNormalizedLinksResolved = ${this._nonNormalizedLinksResolved}`); 103 | } 104 | 105 | return true; 106 | } 107 | 108 | // For any unresolved component URL link for which there is a manifest with the same component 109 | // name, add the link. These links are not guaranteed to be accurate, and there may be 110 | // duplicates, but the provide a best-effort resolution, which may still be helpful. 111 | private _resolveUnmatchedUrlsToAnyMatchingManifestByName() { 112 | for (const [packageAndComponent, locations] of this._packageAndComponentToReferences) { 113 | if (!this._packageAndComponentToManifestUri.get(packageAndComponent)) { 114 | const [ 115 | packageName, 116 | componentName, 117 | ] = packageAndComponent.split('/'); 118 | for (const suffixToRemove of [ 119 | , // undefined (first, try to find a manifestPath without removing any suffix) 120 | /_allowed$/, 121 | /-isolated$/, 122 | /_test$/, 123 | /_tests$/, 124 | ]) { 125 | let lookupByComponentName = componentName; 126 | if (suffixToRemove) { 127 | lookupByComponentName = componentName.replace(suffixToRemove, ''); 128 | if (lookupByComponentName === componentName) { 129 | continue; 130 | } 131 | } 132 | let manifestPath = this._componentToSomeManifestPath.get(lookupByComponentName); 133 | if (!manifestPath && NORMALIZE_WORD_SEPARATORS) { 134 | const normalizedComponentName = _normalize(lookupByComponentName); 135 | if (normalizedComponentName !== lookupByComponentName) { 136 | manifestPath = this._componentToSomeManifestPath.get(normalizedComponentName); 137 | } 138 | } 139 | if (manifestPath) { 140 | this.addLink(packageName, componentName, manifestPath); 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | } 147 | 148 | private async _getLinksToManifests(): Promise { 149 | const componentTargetPathToPackageTargetPaths = new Map(); 150 | const componentTargetPathToComponentNameAndManifest = new Map(); 151 | const componentTargetPathToSubComponentTargets = new Map(); 152 | const packageTargetPathToPackageName = new Map(); 153 | 154 | const ninjaFileUri = this._baseUri.with({ 155 | path: `${this._baseUri.path}/${this._buildDir}/toolchain.ninja` 156 | }); 157 | const ninjaStream = fs.createReadStream(ninjaFileUri.fsPath); 158 | ninjaStream.on('error', (err) => { 159 | log.error( 160 | `Error reading the build dependencies from ${ninjaFileUri.fsPath}: '${err}'\n` + 161 | 'You may need to re-run `fx set ...` and then reload your VS Code window.' 162 | ); 163 | }); 164 | const ninjaReadline = readline.createInterface(ninjaStream); 165 | 166 | let matchedAtLeastOneMetaFarExample = false; 167 | let matchedAtLeastOneManifestAndComponentExample = false; 168 | let matchedAtLeastOnePmBuildExample = false; 169 | 170 | for await (const line of ninjaReadline) { 171 | let result; 172 | if ((result = Provider.extractBuildDirPackageTargetAndComponents(line))) { 173 | const [targetBuildDir, packageTarget, componentTargets] = result; 174 | for (const componentTarget of componentTargets) { 175 | const packageTargetPath = `${targetBuildDir}:${packageTarget}`; 176 | let componentTargetPath; 177 | const [ 178 | subComponentDir, 179 | subComponentTarget, 180 | ] = componentTarget.split('/'); 181 | if (subComponentDir && subComponentTarget) { 182 | componentTargetPath = `${targetBuildDir}/${subComponentDir}:${subComponentTarget}`; 183 | } else { 184 | componentTargetPath = `${targetBuildDir}:${componentTarget}`; 185 | } 186 | if (!matchedAtLeastOneMetaFarExample) { 187 | matchedAtLeastOneMetaFarExample = true; 188 | log.debug( 189 | `Associating packages to components based on build dependencies in ` + 190 | `${ninjaFileUri.fsPath}, for example, package '${packageTarget}' will include at ` + 191 | `least the component built from ninja target '${componentTargetPath}'.` 192 | ); 193 | } 194 | let packageTargetPaths = componentTargetPathToPackageTargetPaths.get(componentTargetPath); 195 | if (!packageTargetPaths) { 196 | packageTargetPaths = []; 197 | componentTargetPathToPackageTargetPaths.set(componentTargetPath, packageTargetPaths); 198 | } 199 | packageTargetPaths.push(packageTargetPath); 200 | } 201 | } else if ((result = Provider.extractSubComponents(line))) { 202 | const [ 203 | manifestPath, 204 | targetBuildDir, 205 | componentTarget, 206 | subComponentTargets, 207 | ] = result; 208 | let componentTargetPath = `${targetBuildDir}:${componentTarget}`; 209 | for (const subComponentTarget of subComponentTargets) { 210 | if (!matchedAtLeastOneMetaFarExample) { 211 | matchedAtLeastOneMetaFarExample = true; 212 | log.debug( 213 | `Associating sub-components to components based on build dependencies in ` + 214 | `${ninjaFileUri.fsPath}, for example, '${componentTargetPath}' will include at ` + 215 | `least the component built from ninja target '${subComponentTarget}'.` 216 | ); 217 | } 218 | let subComponentTargets = 219 | componentTargetPathToSubComponentTargets.get(componentTargetPath); 220 | if (!subComponentTargets) { 221 | subComponentTargets = []; 222 | componentTargetPathToSubComponentTargets.set(componentTargetPath, subComponentTargets); 223 | } 224 | subComponentTargets.push(subComponentTarget); 225 | } 226 | } else if (result = Provider.extractManifestPathAndCmlComponent(line)) { 227 | const [manifestPath, componentName, componentTargetPath] = result; 228 | if (!matchedAtLeastOneManifestAndComponentExample) { 229 | matchedAtLeastOneManifestAndComponentExample = true; 230 | log.debug( 231 | `Matching components to manifests based on build commands in ${ninjaFileUri.fsPath}, ` + 232 | `for example, '${manifestPath}' is the manifest source for ` + 233 | `a component to be named '${componentName}', and built via ninja target ` + 234 | `'${componentTargetPath}'.` 235 | ); 236 | } 237 | 238 | if (log.DEBUG) { 239 | const existing = componentTargetPathToComponentNameAndManifest.get(componentTargetPath); 240 | if (existing) { 241 | const [origComponentName, origManifestPath] = existing; 242 | if (componentName !== origComponentName || 243 | manifestPath !== origManifestPath) { 244 | log.debug( 245 | `WARNING (debug-only check): componentTargetPath '${componentTargetPath}' has ` + 246 | `duplicate entries:\n` + 247 | `${[origComponentName, origManifestPath]} != ` + 248 | `${[componentName, manifestPath]}` 249 | ); 250 | } 251 | } 252 | } 253 | 254 | componentTargetPathToComponentNameAndManifest.set( 255 | componentTargetPath, 256 | [componentName, manifestPath] 257 | ); 258 | } 259 | 260 | // Note that extractBuildDirPackageTargetAndComponents() uses the same 261 | // ninja command line used by extractPackage, so this check is not in 262 | // the if-else-if condition block. 263 | if ((result = Provider.extractPackage(line))) { 264 | const [packageName, packageTargetPath] = result; 265 | if (!matchedAtLeastOnePmBuildExample) { 266 | matchedAtLeastOnePmBuildExample = true; 267 | log.debug( 268 | `Matching package targets to package names based on build commands in ` + 269 | `${ninjaFileUri.fsPath}, for example, '${packageTargetPath}' is the build target for ` + 270 | `a package to be named '${packageName}',` 271 | ); 272 | } 273 | packageTargetPathToPackageName.set(packageTargetPath, packageName); 274 | } 275 | } 276 | 277 | if (!matchedAtLeastOneMetaFarExample) { 278 | log.error( 279 | `The ninja build file '${ninjaFileUri.fsPath}' did not contain any lines matching the ` + 280 | `expected pattern to identify components in a 'build meta.far' statement: \n\n` + 281 | ` metaFarRegEx = ${Provider._metaFarRegEx}\n` 282 | ); 283 | return false; 284 | } else if (!matchedAtLeastOneManifestAndComponentExample) { 285 | log.error( 286 | `The ninja build file '${ninjaFileUri.fsPath}' did not contain any lines matching the ` + 287 | `expected pattern to identify components in a 'validate .cmx manifest' command: \n\n` + 288 | ` cmcCompileCmlRegEx = ${Provider._cmcCompileCmlRegEx}\n` 289 | ); 290 | return false; 291 | } else if (!matchedAtLeastOnePmBuildExample) { 292 | log.error( 293 | `The ninja build file '${ninjaFileUri.fsPath}' did not contain any lines matching the ` + 294 | `expected pattern to identify components in a 'build package' command: \n\n` + 295 | ` pmBuildRegEx = ${Provider._pmBuildRegEx}\n` 296 | ); 297 | return false; 298 | } 299 | 300 | for ( 301 | let [componentTargetPath, packageTargetPaths] 302 | of componentTargetPathToPackageTargetPaths.entries() 303 | ) { 304 | for (const packageTargetPath of packageTargetPaths) { 305 | const packageName = packageTargetPathToPackageName.get(packageTargetPath); 306 | 307 | if (!packageName) { 308 | continue; 309 | } 310 | let componentNameAndManifest = 311 | componentTargetPathToComponentNameAndManifest.get(componentTargetPath); 312 | if (USE_HEURISTICS_TO_FIND_MORE_LINKS) { 313 | if (!componentNameAndManifest) { 314 | const targetWithoutComponentSuffix = componentTargetPath.replace(/:test_/, ':'); 315 | if (targetWithoutComponentSuffix !== componentTargetPath) { 316 | componentTargetPath = targetWithoutComponentSuffix; 317 | componentNameAndManifest = 318 | componentTargetPathToComponentNameAndManifest.get(targetWithoutComponentSuffix); 319 | } 320 | } 321 | if (!componentNameAndManifest) { 322 | const targetWithoutComponentSuffix = componentTargetPath.replace(/_component$/, ''); 323 | if (targetWithoutComponentSuffix !== componentTargetPath) { 324 | componentTargetPath = targetWithoutComponentSuffix; 325 | componentNameAndManifest = 326 | componentTargetPathToComponentNameAndManifest.get(targetWithoutComponentSuffix); 327 | } 328 | } 329 | } 330 | if (!componentNameAndManifest) { 331 | continue; 332 | } 333 | 334 | const [componentName, manifestPath] = componentNameAndManifest; 335 | this.addLink(packageName, componentName, manifestPath); 336 | 337 | const subComponentTargets = 338 | componentTargetPathToSubComponentTargets.get(componentTargetPath); 339 | if (subComponentTargets) { 340 | for (const subComponentTarget of subComponentTargets) { 341 | this.addLink(packageName, subComponentTarget, manifestPath); 342 | } 343 | } 344 | 345 | if (USE_HEURISTICS_TO_FIND_MORE_LINKS) { 346 | const nameWithoutComponentSuffix = 347 | componentName.replace(/_component(_generated_manifest)?$/, ''); 348 | if (nameWithoutComponentSuffix !== componentName) { 349 | const targetWithoutComponentSuffix = 350 | componentTargetPath.replace(/_component(_generated_manifest)?$/, ''); 351 | this.addLink(packageName, nameWithoutComponentSuffix, manifestPath); 352 | } 353 | } 354 | } 355 | } 356 | 357 | log.info('The data required by the DocumentLinkProvider is loaded.'); 358 | return true; 359 | } 360 | 361 | // TODO(#2): These patterns are very fragile and subject to breakage when GN rules change. 362 | // Plus, since they only search the results from `fx set`, the results are limited to the packages 363 | // in the current set of dependencies (which isn't terrible, but not great for general browsing, 364 | // or to find a dependency.) Alternative 1: Find a better way to query the dependencies. 365 | // Alternative 2: Parse the BUILD.gn files (not recommended) And, consider running GN from the 366 | // extension, to generate a custom ninja result, with a broad set of targets, but if possible, a 367 | // narrow set of output targets (only those needed for the extension). 368 | 369 | private static _metaFarRegEx = new RegExp([ 370 | /^\s*build\s*obj\/(?[^.]+?)\/(?[-\w]+)\/meta\.far/, 371 | /\s*(?[^:]*)\s*:/, 372 | /\s*(?[^\s]+)/, 373 | /\s*(?[^|]+)\|/, 374 | /(?(.|\n)*)/, 375 | ].map(r => r.source).join('')); 376 | 377 | static extractBuildDirPackageTargetAndComponents( 378 | line: string 379 | ): [string, string, string[]] | undefined { 380 | const match = Provider._metaFarRegEx.exec(line); 381 | if (!match) { 382 | return; 383 | } 384 | const [ 385 | , // full match 386 | targetBuildDir, 387 | packageTarget, 388 | , // ignoreOtherOutputs 389 | , // ignoreNinjaRulename 390 | , // ignoreInputs 391 | dependencies, 392 | ] = match; 393 | 394 | // Get all dependencies (global search) 395 | const componentTargets = []; 396 | const depRegEx = new RegExp([ 397 | // CAUTION! Since this RegExp is built dynamically, and has at least one capturing group that 398 | // spans a wide swath (multiple lines, as structured here), the typical slash-contained 399 | // JavaScript RegExp syntax cannot be used. This means ALL BACKSLASHES MUST BE DOUBLED. 400 | // Be careful because many editors and parsers do not provide any warnings if you forget 401 | // to add the second backslash, but RegExp parsing will mysteriously stop working as 402 | // expected: 403 | `\\s*obj/${targetBuildDir}(?!/${packageTarget}\\.)/(?:(?:(?:(?${packageTarget})_)?)|(?[^./]+))(?:/)?`, 404 | `(?:`, 405 | `(?:manifest.stamp)|`, 406 | `(?:metadata.stamp)|`, 407 | `(?:validate_manifests[^/]+.stamp)|`, 408 | `(?:[^\\s]+?_component_index.stamp)|`, 409 | `(?[^/]+)(?:\\.manifest)?\\.stamp`, 410 | `)`, 411 | ].join(''), 'g'); 412 | let depMatch; 413 | while ((depMatch = depRegEx.exec(dependencies))) { 414 | let [ 415 | , // full match 416 | componentTargetPrefix, 417 | componentBuildSubdir, 418 | componentTarget, 419 | ] = depMatch; 420 | if (componentTarget === 'component' && componentTargetPrefix) { 421 | componentTarget = `${componentTargetPrefix}_${componentTarget}`; 422 | } 423 | if (componentTarget) { 424 | if (componentBuildSubdir) { 425 | componentTargets.push(`${componentBuildSubdir}/${componentTarget}`); 426 | } else if (componentTarget) { 427 | componentTargets.push(componentTarget); 428 | } 429 | } 430 | } 431 | 432 | return [ 433 | targetBuildDir, 434 | packageTarget, 435 | componentTargets, 436 | ]; 437 | } 438 | 439 | private static _buildCmxRegEx = new RegExp([ 440 | /^\s*build\s*obj\/(?(?[^.]+?)\/(?[-\w]+)\.cm[xl])/, 441 | /\s*(?[^:]*)\s*:/, 442 | /\s*(?[^\s]+)/, 443 | /\s*(?[^|]+)\|/, 444 | /(?(.|\n)*)/, 445 | ].map(r => r.source).join('')); 446 | 447 | static extractSubComponents( 448 | line: string 449 | ): [string, string, string, string[]] | undefined { 450 | const match = Provider._buildCmxRegEx.exec(line); 451 | if (!match) { 452 | return; 453 | } 454 | const [ 455 | , // full match 456 | manifestPath, 457 | targetBuildDir, 458 | componentTarget, 459 | , // ignoreOtherOutputs 460 | , // ignoreNinjaRulename 461 | , // ignoreInputs 462 | dependencies, 463 | ] = match; 464 | 465 | if (manifestPath.startsWith('build/') || manifestPath.endsWith('.cm') || 466 | manifestPath.indexOf('_manifest_compile/') >= 0) { 467 | // generated manifest 468 | return; 469 | } 470 | 471 | // Get all dependencies (global search) 472 | const subComponentTargets = []; 473 | const depRegEx = new RegExp([ 474 | // CAUTION! Since this RegExp is built dynamically, and has at least one capturing group that 475 | // spans a wide swath (multiple lines, as structured here), the typical slash-contained 476 | // JavaScript RegExp syntax cannot be used. This means ALL BACKSLASHES MUST BE DOUBLED. 477 | // Be careful because many editors and parsers do not provide any warnings if you forget 478 | // to add the second backslash, but RegExp parsing will mysteriously stop working as 479 | // expected: 480 | `\\s*obj/${targetBuildDir}/`, 481 | `(?:`, 482 | `(?:${componentTarget}_check_includes)|`, 483 | `(?:${componentTarget}_cmc_validate_references)|`, 484 | `(?:${componentTarget}_manifest_resource)|`, 485 | `(?:${componentTarget}_merge)|`, 486 | `(?:${componentTarget}_validate)|`, 487 | `(?[-\\w]+)`, 488 | `)`, 489 | `\\.stamp`, 490 | ].join(''), 'g'); 491 | let depMatch; 492 | while ((depMatch = depRegEx.exec(dependencies))) { 493 | let [ 494 | , // full match 495 | subComponentTarget, 496 | ] = depMatch; 497 | if (subComponentTarget) { 498 | subComponentTargets.push(subComponentTarget); 499 | } 500 | } 501 | 502 | return [ 503 | manifestPath, 504 | targetBuildDir, 505 | componentTarget, 506 | subComponentTargets, 507 | ]; 508 | } 509 | 510 | private static _cmcCompileCmlRegEx = new RegExp([ 511 | /^\s*command\s*=.*/, 512 | /--label\s+\/\/(?[^$]+)\$:(?[-\w]+)_manifest_compile/, 513 | /[^\s]*\s+obj\/[^\s]+\/(?[^/.]+)\.cm\s/, 514 | /.*\/cmc\s+compile\s+\.\.\/\.\.\/(?[^\s]+)/, 515 | ].map(r => r.source).join('')); 516 | 517 | static extractManifestPathAndCmlComponent(line: string): [string, string, string] | undefined { 518 | const match = Provider._cmcCompileCmlRegEx.exec(line); 519 | if (!match) { 520 | return; 521 | } 522 | 523 | const [ 524 | , // full match 525 | targetBuildDir, 526 | componentTarget, 527 | componentName, 528 | manifestPath, 529 | ] = match; 530 | 531 | if (manifestPath.startsWith('build/') || manifestPath.endsWith('.cm') || 532 | manifestPath.indexOf('_manifest_compile/') >= 0) { 533 | // generated manifest 534 | return; 535 | } 536 | 537 | const componentTargetPath = `${targetBuildDir}:${componentTarget}`; 538 | 539 | return [ 540 | manifestPath, 541 | componentName, 542 | componentTargetPath, 543 | ]; 544 | } 545 | 546 | private static _pmBuildRegEx = new RegExp([ 547 | /^\s*build\s+obj\/(?[^\s]+)\/(?[^\s/]+)\/meta.far\s+/, 548 | /.*package-tool.*\/gn_run_binary.sh\s+[^\s]*\/(?[^\s/.]+)\.stamp/, 549 | ].map(r => r.source).join('')); 550 | 551 | static extractPackage(line: string): [string, string] | undefined { 552 | const match = Provider._pmBuildRegEx.exec(line); 553 | if (!match) { 554 | return; 555 | } 556 | 557 | const [ 558 | , // full match 559 | targetBuildDir, 560 | packageTarget, 561 | packageName, 562 | ] = match; 563 | 564 | const packageTargetPath = `${targetBuildDir}:${packageTarget}`; 565 | 566 | return [ 567 | packageName, 568 | packageTargetPath, 569 | ]; 570 | } 571 | 572 | private async _askGit(gitArgs: string[]): Promise { 573 | let git = child_process.spawnSync( 574 | 'git', 575 | gitArgs, 576 | { cwd: `${this._baseUri.path}` } 577 | ); 578 | 579 | if (git.error) { 580 | log.error( 581 | `Error executing \`git\`: '${git.error}'\n` 582 | ); 583 | return; 584 | } 585 | 586 | if (git.status !== 0) { 587 | log.error( 588 | `Error (${git.status}) executing \`git\`: '${git.stderr}'\n` 589 | ); 590 | return; 591 | } 592 | 593 | return git.stdout.toString(); 594 | } 595 | 596 | private async _gitLsFiles(gitLsFilesGlobs: string[]): Promise { 597 | let gitArgs = [ 598 | '--no-pager', 599 | '--glob-pathspecs', 600 | 'ls-files', 601 | '--cached', // ensure cached mode (normally the default) 602 | '--recurse-submodules', // requires --cached 603 | '--', 604 | ]; 605 | 606 | gitArgs = gitArgs.concat(gitLsFilesGlobs); 607 | 608 | log.info( 609 | `Searching the 'fuchsia.git' repo, by running the command: \n\n` + 610 | ` \`git ${gitArgs.join(' ')}\`\n\n` + 611 | `from the '${this._baseUri.path}' directory.` 612 | ); 613 | 614 | return this._askGit(gitArgs); 615 | } 616 | 617 | // Note, this private function may be useful for experimentation, or a future feature, but is 618 | // currently unused. 619 | private async _findAllManifests(): Promise { 620 | return (await this._gitLsFiles([ 621 | '**/?*.cmx', 622 | '**/?*.cml', 623 | ]) ?? '').split('\n'); 624 | } 625 | 626 | private async _gitGrep(grepExtendedRegEx: string | RegExp): Promise { 627 | if (grepExtendedRegEx instanceof RegExp) { 628 | grepExtendedRegEx = grepExtendedRegEx.source; 629 | } 630 | 631 | const gitArgs = [ 632 | '--no-pager', 633 | 'grep', 634 | '--recurse-submodules', 635 | '-I', // don't match the pattern in binary files 636 | '--extended-regexp', 637 | // '--only-matching', // grep BUG! --column value is wrong for second match in line 638 | // '--column', // not useful without --only-matching 639 | '--line-number', 640 | '--no-column', 641 | '--no-color', 642 | grepExtendedRegEx, 643 | ]; 644 | 645 | log.info( 646 | `Searching the 'fuchsia.git' repo, by running the command: \n\n` + 647 | ` \`git ${gitArgs.join(' ')}\`\n\n` + 648 | `from the '${this._baseUri.path}' directory.` 649 | ); 650 | 651 | return this._askGit(gitArgs); 652 | } 653 | 654 | private async _getReferencesToManifests(): Promise { 655 | 656 | log.info( 657 | `Searching for component URLs('fuchsia-pkg://...cm[x]') referenced from any text document.` 658 | ); 659 | 660 | const matches = await this._gitGrep(/fuchsia-pkg:\/\/fuchsia.com\/([^#]*)#meta\/(-|\w)*\.cmx?/); 661 | if (!matches) { 662 | return false; 663 | } 664 | 665 | // patterns end in either '.cm' or '.cmx' 666 | const urlRegEx = /\bfuchsia-pkg:\/\/fuchsia.com\/([-\w]+)(?:\?[^#]*)?#meta\/([-\w]+)\.cmx?\b/g; 667 | 668 | let loggedAtLeastOneExample = false; 669 | 670 | let start = 0; 671 | while (start < matches.length) { 672 | let end = matches.indexOf('\n', start); 673 | if (end === -1) { 674 | end = matches.length; 675 | } 676 | const line = matches.substr(start, end - start); 677 | start = end + 1; 678 | const [path, lineNumberStr] = line.split(':', 2); 679 | const lineNumber: number = (+lineNumberStr) - 1; 680 | const matchedLine = line.substr(path.length + 1 + lineNumberStr.length); 681 | let match; 682 | while ((match = urlRegEx.exec(matchedLine))) { 683 | const componentUrl = match[0]; 684 | const packageName = match[1]; 685 | const componentName = match[2]; 686 | const column = match.index - 1; 687 | const sourceUri = this._baseUri.with({ path: `${this._baseUri.path}/${path}` }); 688 | this.addReference(packageName, componentName, componentUrl, sourceUri, lineNumber, column); 689 | if (!loggedAtLeastOneExample) { 690 | loggedAtLeastOneExample = true; 691 | log.debug([ 692 | `Getting references to manifests. For example, '${componentUrl}' is referenced by `, 693 | `'${sourceUri.fsPath}:`, 694 | `${lineNumber + 1}:`, 695 | `${column + 1}:`, 696 | `${lineNumber + 1}:`, 697 | `${column + componentUrl.length + 1}`, 698 | ].join('')); 699 | } 700 | } 701 | if (!loggedAtLeastOneExample) { 702 | loggedAtLeastOneExample = true; 703 | log.warn( 704 | `RegEx failed to match the first line returned from \`git grep\`.\n\n` + 705 | ` Line: '${matchedLine}'\n` + 706 | ` RegEx: ${urlRegEx}` 707 | ); 708 | } 709 | } 710 | 711 | if (loggedAtLeastOneExample) { 712 | log.info('The data required by the ReferenceProvider is loaded.'); 713 | } else { 714 | log.error( 715 | `No component URLs ('fuchsia-pkg://...cm[x]') were found in the 'fuchsia.git' repo` 716 | ); 717 | } 718 | return true; 719 | } 720 | 721 | // TODO(#3): find links to fuchsia Service declarations in .fidl files using (I suggest) 722 | // a `git` command (since we know this works) equivalent of: 723 | // $ find ${this._baseUri}/${buildDir} -name '*fidl.json' 724 | // 725 | // for each matched file, use VS Code JSON parsing APIs to do the equivalent of: 726 | // $ jq '.interface_declarations[] | .name,.location' \ 727 | // ${this._baseUri}/${buildDir}/fidling/gen/sdk/fidl/fuchsia.logger/fuchsia.logger.fidl.json 728 | // "fuchsia.logger/Log" 729 | // { 730 | // "filename": "../../sdk/fidl/fuchsia.logger/logger.fidl", 731 | // "line": 114, 732 | // "column": 10, 733 | // "length": 3 734 | // } 735 | // "fuchsia.logger/LogSink" 736 | // { 737 | // "filename": "../../sdk/fidl/fuchsia.logger/logger.fidl", 738 | // "line": 140, 739 | // "column": 10, 740 | // "length": 7 741 | // } 742 | // 743 | // And use that information to add additional service links via the provideDocumentLinks() 744 | // method. 745 | 746 | private static _findLinksOredRegEx = new RegExp( 747 | '(?:(?:' + 748 | [ 749 | // Note the suffix `.cmx?`: component URLs end in either '.cm' or '.cmx' 750 | /\bfuchsia-pkg:\/\/fuchsia.com\/(?[-\w]+)(?:\?[^#]*)?#meta\/(?[-\w]+)\.cmx?\b/, 751 | /(?:https?:\/\/)?fxr(?:ev.dev)?\/(?\d+)/, 752 | /(?:https?:\/\/)?fxb(?:ug.dev)?\/(?\d+)/, 753 | ].map(r => r.source).join(')|(?:') 754 | + '))', 755 | 'g' 756 | ); 757 | 758 | private _findLinks(text: string): FuchsiAwareLink[] { 759 | const links: FuchsiAwareLink[] = []; 760 | let match; 761 | while ((match = Provider._findLinksOredRegEx.exec(text))) { 762 | const [ 763 | fullMatch, 764 | packageName, 765 | componentName, 766 | revId, 767 | bugId, 768 | ] = match; 769 | const startIndex = match.index; 770 | const length = fullMatch.length; 771 | const webPath = revId ? `fxrev.dev/${revId}` : `fxbug.dev/${bugId}`; 772 | let tooltip: string | undefined; 773 | let uri: vscode.Uri | undefined; 774 | if (packageName && componentName) { 775 | const packageAndComponent = `${packageName}/${componentName}`; 776 | uri = this._packageAndComponentToManifestUri.get(packageAndComponent); 777 | if (NORMALIZE_WORD_SEPARATORS && !uri) { 778 | uri = this._packageAndComponentToManifestUri.get(_normalize(packageAndComponent)); 779 | } 780 | if (uri) { 781 | tooltip = 'Open component manifest'; 782 | } else if (SHOW_UNRESOLVED_TERMINAL_LINKS) { 783 | tooltip = 'Manifest not found!'; 784 | } else { 785 | continue; // don't add the link 786 | } 787 | } else if (webPath) { 788 | uri = vscode.Uri.parse(`https://${webPath}`); 789 | } 790 | links.push({ startIndex, length, tooltip, uri }); 791 | } 792 | return links; 793 | } 794 | 795 | provideReferences( 796 | document: vscode.TextDocument, 797 | position: vscode.Position, 798 | context: vscode.ReferenceContext, 799 | token: vscode.CancellationToken, 800 | ): vscode.Location[] | undefined { 801 | const ext = document.uri.path.split('.').slice(-1)[0]; 802 | // For unit testing, the document is virtual, and will be untitled, but we can check its 803 | // languageId. Otherwise, check its extension. For real manifest files, the language ID may 804 | // be json or json5. 805 | if (document.languageId !== 'untitled-fuchsia-manifest' && ext !== 'cml' && ext !== 'cmx') { 806 | return; 807 | } 808 | let sourcePath = document.uri.fsPath; 809 | if (document.uri.fsPath.startsWith(this._baseUri.fsPath)) { 810 | sourcePath = sourcePath.replace(this._baseUri.fsPath + '/', ''); 811 | } 812 | let references = new Set(); 813 | for (let packageAndComponent of this._sourcePathToPackageAndComponents.get(sourcePath) ?? []) { 814 | if (NORMALIZE_WORD_SEPARATORS) { 815 | packageAndComponent = _normalize(packageAndComponent); 816 | } 817 | this._packageAndComponentToReferences.get( 818 | packageAndComponent 819 | )?.forEach(reference => references.add(reference)); 820 | } 821 | return Array.from(references.values()); 822 | } 823 | 824 | provideDocumentLinks( 825 | document: vscode.TextDocument, 826 | token: vscode.CancellationToken, 827 | ): vscode.DocumentLink[] | undefined { 828 | const documentLinks: vscode.DocumentLink[] = []; 829 | for (const link of this._findLinks(document.getText())) { 830 | if (link.uri) { 831 | const startPos = document.positionAt(link.startIndex); 832 | const endPos = document.positionAt(link.startIndex + link.length); 833 | const linkRange = new vscode.Range(startPos, endPos); 834 | documentLinks.push(new vscode.DocumentLink(linkRange, link.uri)); 835 | } 836 | } 837 | return documentLinks; 838 | } 839 | 840 | provideTerminalLinks( 841 | context: vscode.TerminalLinkContext, 842 | token: vscode.CancellationToken 843 | ): vscode.TerminalLink[] | undefined { 844 | return this._findLinks(context.line); 845 | } 846 | 847 | handleTerminalLink(link: FuchsiAwareLink) { 848 | if (!link.uri) { 849 | return; 850 | } 851 | if (['http', 'https'].indexOf(link.uri.scheme) >= 0) { 852 | vscode.env.openExternal(link.uri); 853 | } else { 854 | const document = vscode.workspace.openTextDocument(link.uri).then(document => { 855 | vscode.window.showTextDocument(document); 856 | }); 857 | } 858 | } 859 | 860 | addLink(packageName: string, componentName: string, manifestPath: string) { 861 | let packageAndComponent = `${packageName}/${componentName}`; 862 | 863 | let manifestFileName = manifestPath.split('/').slice(-1)[0]; 864 | let packageAndComponents = this._manifestFileNameToPackageAndComponents.get(manifestFileName); 865 | if (!packageAndComponents) { 866 | packageAndComponents = []; 867 | this._manifestFileNameToPackageAndComponents.set(packageAndComponent, packageAndComponents); 868 | } 869 | packageAndComponents.push(packageAndComponent); 870 | 871 | if (manifestPath.indexOf(`/${componentName}.cm`) >= 0) { 872 | this._componentToSomeManifestPath.set(componentName, manifestPath); 873 | } 874 | if (this._addLinkToMap(packageAndComponent, manifestPath)) { 875 | this._nonNormalizedLinksResolved++; 876 | } 877 | if (NORMALIZE_WORD_SEPARATORS) { 878 | const normalizedPackageAndComponent = _normalize(packageAndComponent); 879 | if (normalizedPackageAndComponent !== packageAndComponent) { 880 | const normalizedComponentName = _normalize(componentName); 881 | if (normalizedComponentName !== componentName) { 882 | if (_normalize(manifestPath).indexOf(`/${normalizedComponentName}.cm`) >= 0) { 883 | this._componentToSomeManifestPath.set(normalizedComponentName, manifestPath); 884 | } 885 | } 886 | this._addLinkToMap(normalizedPackageAndComponent, manifestPath); 887 | } 888 | } 889 | } 890 | 891 | private _addLinkToMap(packageAndComponent: string, manifestPath: string): boolean { 892 | let linkWasAdded = false; 893 | if (!this._packageAndComponentToManifestUri.get(packageAndComponent)) { 894 | linkWasAdded = true; 895 | const manifestUri = this._baseUri.with({ path: `${this._baseUri.path}/${manifestPath}` }); 896 | this._packageAndComponentToManifestUri.set(packageAndComponent, manifestUri); 897 | } 898 | let packageAndComponents = this._sourcePathToPackageAndComponents.get(manifestPath); 899 | if (!packageAndComponents) { 900 | packageAndComponents = []; 901 | this._sourcePathToPackageAndComponents.set(manifestPath, packageAndComponents); 902 | } 903 | packageAndComponents.push(packageAndComponent); 904 | return linkWasAdded; 905 | } 906 | 907 | addReference( 908 | packageName: string, 909 | componentName: string, 910 | componentUrl: string, 911 | referencedByUri: vscode.Uri, 912 | lineNumber: number, 913 | column: number, 914 | ) { 915 | const packageAndComponent = `${packageName}/${componentName}`; 916 | const range = new vscode.Range( 917 | lineNumber, 918 | column, 919 | lineNumber, 920 | column + componentUrl.length, 921 | ); 922 | this._addReferenceToMap(packageAndComponent, referencedByUri, range); 923 | if (NORMALIZE_WORD_SEPARATORS) { 924 | const normalizedPackageAndComponent = _normalize(packageAndComponent); 925 | if (normalizedPackageAndComponent !== packageAndComponent) { 926 | this._addReferenceToMap(normalizedPackageAndComponent, referencedByUri, range); 927 | } 928 | } 929 | } 930 | 931 | private _addReferenceToMap( 932 | packageAndComponent: string, 933 | referencedByUri: vscode.Uri, 934 | range: vscode.Range, 935 | ) { 936 | let references = this._packageAndComponentToReferences.get(packageAndComponent); 937 | if (!references) { 938 | references = []; 939 | this._packageAndComponentToReferences.set(packageAndComponent, references); 940 | } 941 | references.push(new vscode.Location(referencedByUri, range)); 942 | } 943 | } 944 | 945 | function _normalize(nameOrTarget: string): string { 946 | return nameOrTarget.replace(/-/g, '_'); 947 | } 948 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as path from 'path'; 16 | 17 | import { runTests } from 'vscode-test'; 18 | 19 | async function main() { 20 | try { 21 | // The folder containing the Extension Manifest package.json 22 | // Passed to `--extensionDevelopmentPath` 23 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 24 | 25 | // The path to test runner 26 | // Passed to --extensionTestsPath 27 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 28 | 29 | // Download VS Code, unzip it and run the integration test 30 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 31 | } catch (err) { 32 | console.error('Failed to run tests'); 33 | process.exit(1); 34 | } 35 | } 36 | 37 | main(); 38 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as assert from 'assert'; 16 | 17 | import * as vscode from 'vscode'; 18 | import { Provider } from '../../provider'; 19 | 20 | suite('Extension Test Suite', () => { 21 | vscode.window.showInformationMessage('Start all tests.'); 22 | 23 | const baseUri = vscode.Uri.file('fuchsia'); 24 | const buildDir = 'out/default.test'; 25 | 26 | // TODO(#4): Replace these hardcoded copies of specific lines from the `toolchain.ninja` 27 | // file with a cached but quickly refreshed copy of the developer's most current `toolchain.ninja` 28 | // (at least the subset of lines relevant to this extension). Each 'line' value below can then 29 | // be replaced with a regex to pull the specific required line from the cached and current data. 30 | // This way the test can validate that the expected format has not changed. 31 | 32 | // TODO(#5): Add an integration test that loops throught all extracted packageAndComponent 33 | // pairs, formats them into 'fuchsia-pkg://...' URLs, gets the manifest URIs from 34 | // provideDocumentLinks(), and validates the files exist. Generate stats for the number of 35 | // valid and invalid links. 36 | 37 | ////////////////////////////////////////////////////////////////////////////////////////////////// 38 | test('libdriver_integration_test_extractBuildDirPackageTargetAndComponents', () => { 39 | const line = ` 40 | build 41 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test/meta.far 42 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test/meta/contents 43 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test/meta.far.merkle 44 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test/blobs.json 45 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test/blobs.manifest 46 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test/package_manifest.json: 47 | __src_devices_tests_libdriver-integration-test_libdriver-integration-test___build_toolchain_fuchsia_arm64__rule 48 | | 49 | ../../build/gn_run_binary.sh 50 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test_manifest 51 | host_x64/pm 52 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test_component.stamp 53 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test_manifest.stamp 54 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test_metadata.stamp 55 | obj/src/devices/tests/libdriver-integration-test/libdriver-integration-test_test_libdriver-integration-test_component.stamp 56 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 57 | `.replace(/\n/g, ' '); 58 | 59 | const [ 60 | targetBuildDir, 61 | packageTarget, 62 | componentTargets, 63 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 64 | assert.strictEqual(targetBuildDir, 'src/devices/tests/libdriver-integration-test'); 65 | assert.strictEqual(packageTarget, 'libdriver-integration-test'); 66 | assert.deepStrictEqual(componentTargets, [ 67 | 'libdriver-integration-test_component', 68 | 'test_libdriver-integration-test_component', 69 | ]); 70 | }); 71 | 72 | ////////////////////////////////////////////////////////////////////////////////////////////////// 73 | test('archivist_integration_tests_extractBuildDirPackageTargetAndComponents', () => { 74 | const line = ` 75 | build 76 | obj/src/devices/bin/driver_manager/driver-manager-tests/meta.far 77 | obj/src/devices/bin/driver_manager/driver-manager-tests/meta/contents 78 | obj/src/devices/bin/driver_manager/driver-manager-tests/meta.far.merkle 79 | obj/src/devices/bin/driver_manager/driver-manager-tests/blobs.json 80 | obj/src/devices/bin/driver_manager/driver-manager-tests/blobs.manifest 81 | obj/src/devices/bin/driver_manager/driver-manager-tests/package_manifest.json: 82 | __src_devices_bin_driver_manager_driver-manager-tests___build_toolchain_fuchsia_arm64__rule 83 | | 84 | ../../build/gn_run_binary.sh 85 | obj/src/devices/bin/driver_manager/driver-manager-tests_manifest 86 | host_x64/pm 87 | obj/src/devices/bin/driver_manager/driver-host-loader-service-test.stamp 88 | obj/src/devices/bin/driver_manager/driver-manager-test.stamp 89 | obj/src/devices/bin/driver_manager/driver-manager-tests_manifest.stamp 90 | obj/src/devices/bin/driver_manager/driver-manager-tests_metadata.stamp 91 | obj/src/devices/bin/driver_manager/driver-manager-tests_test_driver-host-loader-service-test.stamp 92 | obj/src/devices/bin/driver_manager/driver-manager-tests_test_driver-manager-test.stamp 93 | obj/src/devices/bin/driver_manager/driver-manager-tests_test_driver-runner-test.stamp 94 | obj/src/devices/bin/driver_manager/driver-runner-test.stamp 95 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 96 | `.replace(/\n/g, ' '); 97 | 98 | const [ 99 | targetBuildDir, 100 | packageTarget, 101 | componentTargets, 102 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 103 | assert.strictEqual(targetBuildDir, 'src/devices/bin/driver_manager'); 104 | assert.strictEqual(packageTarget, 'driver-manager-tests'); 105 | assert.deepStrictEqual(componentTargets, [ 106 | 'driver-host-loader-service-test', 107 | 'driver-manager-test', 108 | 'test_driver-host-loader-service-test', 109 | 'test_driver-manager-test', 110 | 'test_driver-runner-test', 111 | 'driver-runner-test', 112 | ]); 113 | }); 114 | 115 | ////////////////////////////////////////////////////////////////////////////////////////////////// 116 | test('archivist_integration_tests_extractBuildDirPackageTargetAndComponents', () => { 117 | const line = ` 118 | build 119 | obj/src/diagnostics/archivist/tests/archive_path/archive_path.cmx: 120 | __src_diagnostics_archivist_tests_archive_path_archive_path___build_toolchain_fuchsia_arm64__rule 121 | | 122 | ../../build/gn_run_binary.sh 123 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_merge 124 | host_x64/cmc 125 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_check_includes.stamp 126 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_cmc_validate_references.stamp 127 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_include.cmx.stamp 128 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_manifest_resource.stamp 129 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_merge.stamp 130 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_test_archivist.stamp 131 | obj/src/diagnostics/archivist/tests/archive_path/archive_path_validate.stamp 132 | ./archive_path_test 133 | host_x64/cmc 134 | `.replace(/\n/g, ' '); 135 | 136 | const [ 137 | manifestSourcePath, 138 | targetBuildDir, 139 | componentTarget, 140 | subComponentTargets, 141 | ] = Provider.extractSubComponents(line) ?? [, , []]; 142 | assert.strictEqual( 143 | manifestSourcePath, 144 | 'src/diagnostics/archivist/tests/archive_path/archive_path.cmx' 145 | ); 146 | assert.strictEqual(targetBuildDir, 'src/diagnostics/archivist/tests/archive_path'); 147 | assert.strictEqual(componentTarget, 'archive_path'); 148 | assert.deepStrictEqual(subComponentTargets, [ 149 | 'archive_path_test_archivist', 150 | ]); 151 | }); 152 | 153 | ////////////////////////////////////////////////////////////////////////////////////////////////// 154 | test('archivist_integration_tests_extractBuildDirPackageTargetAndComponents', () => { 155 | const line = ` 156 | build 157 | obj/src/diagnostics/archivist/tests/archivist-integration-tests/meta.far 158 | obj/src/diagnostics/archivist/tests/archivist-integration-tests/meta/contents 159 | obj/src/diagnostics/archivist/tests/archivist-integration-tests/meta.far.merkle 160 | obj/src/diagnostics/archivist/tests/archivist-integration-tests/blobs.json 161 | obj/src/diagnostics/archivist/tests/archivist-integration-tests/blobs.manifest 162 | obj/src/diagnostics/archivist/tests/archivist-integration-tests/package_manifest.json: 163 | __src_diagnostics_archivist_tests_archivist-integration-tests___build_toolchain_fuchsia_arm64__rule 164 | | 165 | ../../build/gn_run_binary.sh 166 | obj/src/diagnostics/archivist/tests/archivist-integration-tests_manifest 167 | host_x64/pm 168 | obj/src/diagnostics/archivist/tests/archivist-integration-tests_manifest.stamp 169 | obj/src/diagnostics/archivist/tests/archivist-integration-tests_metadata.stamp 170 | obj/src/diagnostics/archivist/tests/accessor_truncation/accessor-truncation-integration-test.stamp 171 | obj/src/diagnostics/archivist/tests/archive_path/archive_path.stamp 172 | obj/src/diagnostics/archivist/tests/feedback_reader/feedback_reader.stamp 173 | obj/src/diagnostics/archivist/tests/logs/cpp/cpp.stamp 174 | obj/src/diagnostics/archivist/tests/unified_reader/unified_reader.stamp 175 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 176 | `.replace(/\n/g, ' '); 177 | 178 | const [ 179 | targetBuildDir, 180 | packageTarget, 181 | componentTargets, 182 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 183 | assert.strictEqual(targetBuildDir, 'src/diagnostics/archivist/tests'); 184 | assert.strictEqual(packageTarget, 'archivist-integration-tests'); 185 | assert.deepStrictEqual(componentTargets, [ 186 | 'accessor_truncation/accessor-truncation-integration-test', 187 | 'archive_path/archive_path', 188 | 'feedback_reader/feedback_reader', 189 | 'unified_reader/unified_reader', 190 | ]); 191 | }); 192 | 193 | ////////////////////////////////////////////////////////////////////////////////////////////////// 194 | test('session_manager_extractBuildDirPackageTargetAndComponents', () => { 195 | const line = ` 196 | build 197 | obj/src/session/bin/session_manager/session_manager/meta.far 198 | obj/src/session/bin/session_manager/session_manager/meta/contents 199 | obj/src/session/bin/session_manager/session_manager/meta.far.merkle 200 | obj/src/session/bin/session_manager/session_manager/blobs.json 201 | obj/src/session/bin/session_manager/session_manager/blobs.manifest 202 | obj/src/session/bin/session_manager/session_manager/package_manifest.json: 203 | __src_session_bin_session_manager_session_manager___build_toolchain_fuchsia_arm64__rule 204 | | 205 | ../../build/gn_run_binary.sh 206 | obj/src/session/bin/session_manager/session_manager_manifest 207 | host_x64/pm 208 | obj/src/session/bin/session_manager/session_manager_component.stamp 209 | obj/src/session/bin/session_manager/session_manager_manifest.stamp 210 | obj/src/session/bin/session_manager/session_manager_metadata.stamp 211 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 212 | `.replace(/\n/g, ' '); 213 | 214 | const [ 215 | targetBuildDir, packageTarget, componentTargets, 216 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 217 | assert.strictEqual(targetBuildDir, 'src/session/bin/session_manager'); 218 | assert.strictEqual(packageTarget, 'session_manager'); 219 | assert.deepStrictEqual(componentTargets, [ 220 | 'session_manager_component', 221 | ]); 222 | }); 223 | 224 | ////////////////////////////////////////////////////////////////////////////////////////////////// 225 | test('ime_keyboard_test_extractBuildDirPackageTargetAndComponents', () => { 226 | const line = ` 227 | build 228 | obj/src/ui/bin/ime/keyboard_test/meta.far 229 | obj/src/ui/bin/ime/keyboard_test/meta/contents 230 | obj/src/ui/bin/ime/keyboard_test/meta.far.merkle 231 | obj/src/ui/bin/ime/keyboard_test/blobs.json 232 | obj/src/ui/bin/ime/keyboard_test/blobs.manifest 233 | obj/src/ui/bin/ime/keyboard_test/package_manifest.json: 234 | __src_ui_bin_ime_keyboard_test___build_toolchain_fuchsia_arm64__rule 235 | | ../../build/gn_run_binary.sh 236 | obj/src/ui/bin/ime/keyboard_test.manifest 237 | host_x64/pm 238 | obj/build/deprecated_package.stamp 239 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 240 | ./default_hardware_ime 241 | ./ime_service 242 | ./ime_service_integration_test 243 | ./keyboard3_integration_test 244 | obj/src/ui/bin/ime/keyboard_test.manifest.stamp 245 | obj/src/ui/bin/ime/keyboard_test.resource.resource.goldens_en-us.json.stamp 246 | obj/src/ui/bin/ime/keyboard_test.resource.resource.us.json.stamp 247 | ./keyboard_test_bin 248 | obj/src/ui/bin/ime/keyboard_test_default_hardware_ime.cmx.stamp 249 | obj/src/ui/bin/ime/keyboard_test_default_hardware_ime.cmx_component_index.stamp 250 | obj/src/ui/bin/ime/keyboard_test_ime_service.cmx.stamp 251 | obj/src/ui/bin/ime/keyboard_test_ime_service.cmx_component_index.stamp 252 | obj/src/ui/bin/ime/keyboard_test_ime_service_integration_test.cmx.stamp 253 | obj/src/ui/bin/ime/keyboard_test_ime_service_integration_test.cmx_component_index.stamp 254 | obj/src/ui/bin/ime/keyboard_test_keyboard3_integration_test.cmx.stamp 255 | obj/src/ui/bin/ime/keyboard_test_keyboard3_integration_test.cmx_component_index.stamp 256 | obj/src/ui/bin/ime/keyboard_test_keyboard_test_bin.cmx.stamp 257 | obj/src/ui/bin/ime/keyboard_test_keyboard_test_bin.cmx_component_index.stamp 258 | obj/src/ui/bin/ime/keyboard_test_metadata.stamp 259 | obj/src/ui/bin/ime/keyboard_test_test/ime_service_integration_test_test_spec.stamp 260 | obj/src/ui/bin/ime/keyboard_test_test/keyboard3_integration_test_test_spec.stamp 261 | obj/src/ui/bin/ime/keyboard_test_test/keyboard_test_bin_test_spec.stamp 262 | obj/src/ui/bin/ime/keyboard_test_validate_manifests_default_hardware_ime.cmx.stamp 263 | obj/src/ui/bin/ime/keyboard_test_validate_manifests_ime_service.cmx.stamp 264 | obj/src/ui/bin/ime/keyboard_test_validate_manifests_ime_service_integration_test.cmx.stamp 265 | obj/src/ui/bin/ime/keyboard_test_validate_manifests_keyboard3_integration_test.cmx.stamp 266 | obj/src/ui/bin/ime/keyboard_test_validate_manifests_keyboard_test_bin.cmx.stamp 267 | `.replace(/\n/g, ' '); 268 | 269 | const [ 270 | targetBuildDir, packageTarget, componentTargets, 271 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 272 | assert.strictEqual(targetBuildDir, 'src/ui/bin/ime'); 273 | assert.strictEqual(packageTarget, 'keyboard_test'); 274 | assert.deepStrictEqual(componentTargets, [ 275 | 'default_hardware_ime.cmx', 276 | 'ime_service.cmx', 277 | 'ime_service_integration_test.cmx', 278 | 'keyboard3_integration_test.cmx', 279 | 'keyboard_test_bin.cmx', 280 | 'keyboard_test_test/ime_service_integration_test_test_spec', 281 | 'keyboard_test_test/keyboard3_integration_test_test_spec', 282 | 'keyboard_test_test/keyboard_test_bin_test_spec', 283 | ]); 284 | }); 285 | 286 | ////////////////////////////////////////////////////////////////////////////////////////////////// 287 | test('archivist_integration_tests_extractPackage', () => { 288 | const line = ` 289 | build 290 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests/meta.far 291 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests/meta.far.merkle 292 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests/blobs.json 293 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests/blobs.manifest 294 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests/package_manifest.json: 295 | __src_diagnostics_archivist_tests_integration_archivist-integration-tests.pm___build_toolchain_fuchsia_x64__rule 296 | | 297 | ../../build/rbe/output-scanner.sh 298 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests_manifest 299 | host_x64/package-tool 300 | ../../build/gn_run_binary.sh 301 | obj/src/diagnostics/archivist/tests/integration/archivist-for-integration.stamp 302 | obj/src/diagnostics/archivist/tests/integration/archivist-for-integration-config.stamp 303 | obj/src/diagnostics/archivist/tests/integration/archivist-for-integration-for-v1.stamp 304 | obj/src/diagnostics/archivist/tests/integration/archivist-for-v1-config.stamp 305 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests.pm_metadata.stamp 306 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests.verify.stamp 307 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests_manifest.stamp 308 | obj/src/diagnostics/archivist/tests/integration/archivist-integration-tests_test_archivist_integration_tests.stamp 309 | obj/src/diagnostics/archivist/tests/integration/archivist-with-feedback-filtering.stamp 310 | obj/src/diagnostics/archivist/tests/integration/archivist-with-feedback-filtering-config.stamp 311 | obj/src/diagnostics/archivist/tests/integration/archivist-with-feedback-filtering-disabled.stamp 312 | obj/src/diagnostics/archivist/tests/integration/archivist-with-feedback-filtering-disabled-config.stamp 313 | obj/src/diagnostics/archivist/tests/integration/archivist-with-klog.stamp 314 | obj/src/diagnostics/archivist/tests/integration/archivist-with-klog-config.stamp 315 | obj/src/diagnostics/archivist/tests/integration/archivist-with-legacy-metrics-filtering.stamp 316 | obj/src/diagnostics/archivist/tests/integration/archivist-with-legacy-metrics-filtering-config.stamp 317 | obj/src/diagnostics/archivist/tests/integration/archivist-with-lowpan-filtering.stamp 318 | obj/src/diagnostics/archivist/tests/integration/archivist-with-lowpan-filtering-config.stamp 319 | obj/src/diagnostics/archivist/tests/integration/archivist-with-small-caches.stamp 320 | obj/src/diagnostics/archivist/tests/integration/archivist-with-small-caches-config.stamp 321 | obj/src/diagnostics/archivist/tests/integration/archivist_integration_tests.stamp 322 | obj/src/diagnostics/archivist/tests/integration/configure_legacy_metrics_pipeline.stamp 323 | obj/src/diagnostics/archivist/tests/integration/do_not_filter_feedback.stamp 324 | obj/src/diagnostics/archivist/tests/integration/filter_feedback.stamp 325 | obj/src/diagnostics/archivist/tests/integration/filter_lowpan.stamp 326 | obj/src/diagnostics/archivist/tests/integration/components/components.stamp 327 | obj/src/diagnostics/iquery/test/test_component/test_component.stamp 328 | host_x64/obj/src/sys/pkg/bin/package-tool/package-tool.stamp 329 | `.replace(/\n/g, ' '); 330 | 331 | const [ 332 | packageName, packageTargetPath 333 | ] = Provider.extractPackage(line) ?? []; 334 | assert.strictEqual(packageName, 'archivist-for-integration'); 335 | assert.strictEqual( 336 | packageTargetPath, 337 | 'src/diagnostics/archivist/tests/integration:archivist-integration-tests' 338 | ); 339 | }); 340 | 341 | ////////////////////////////////////////////////////////////////////////////////////////////////// 342 | test('archivist_integration_tests_v2_extractManifestPathAndCmlComponent', () => { 343 | const line = ` 344 | command 345 | = 346 | 347 | ../../build/rbe/output-scanner.sh 348 | --label 349 | //src/diagnostics/archivist/tests/integration$:archivist_integration_tests_manifest_compile\(//build/toolchain/fuchsia$:x64\) 350 | obj/src/diagnostics/archivist/tests/integration/cml/archivist_integration_tests_manifest_compile/archivist_integration_tests.cm 351 | -- 352 | ../../build/gn_run_binary.sh 353 | ../../prebuilt/third_party/clang/linux-x64/bin 354 | host_x64/cmc 355 | compile 356 | ../../src/diagnostics/archivist/tests/integration/meta/archivist_integration_tests.cml 357 | --output 358 | obj/src/diagnostics/archivist/tests/integration/cml/archivist_integration_tests_manifest_compile/archivist_integration_tests.cm 359 | --depfile 360 | obj/src/diagnostics/archivist/tests/integration/archivist_integration_tests_manifest_compile.d 361 | --config-package-path 362 | meta/archivist_integration_tests.cvf 363 | --includeroot 364 | ../../ 365 | --includepath 366 | ../../sdk/lib/ 367 | --features 368 | hub 369 | `.replace(/\n/g, ' '); 370 | 371 | const [ 372 | manifestSourcePath, componentName, componentTargetPath 373 | ] = Provider.extractManifestPathAndCmlComponent(line) ?? []; 374 | assert.strictEqual( 375 | manifestSourcePath, 376 | 'src/diagnostics/archivist/tests/integration/meta/archivist_integration_tests.cml' 377 | ); 378 | assert.strictEqual(componentName, 'archivist_integration_tests'); 379 | assert.strictEqual( 380 | componentTargetPath, 381 | 'src/diagnostics/archivist/tests/integration:archivist_integration_tests' 382 | ); 383 | }); 384 | 385 | ////////////////////////////////////////////////////////////////////////////////////////////////// 386 | test('archivist_integration_tests_v2_extractBuildDirPackageTargetAndComponents', () => { 387 | const line = ` 388 | build 389 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2/meta.far 390 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2/meta/contents 391 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2/meta.far.merkle 392 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2/blobs.json 393 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2/blobs.manifest 394 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2/package_manifest.json: 395 | __src_diagnostics_archivist_tests_v2_archivist-integration-tests-v2___build_toolchain_fuchsia_arm64__rule 396 | | 397 | ../../build/gn_run_binary.sh 398 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2.manifest 399 | host_x64/pm 400 | obj/build/deprecated_package.stamp 401 | ./archivist 402 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2.manifest.stamp 403 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2.resource.resource.config_archivist_config.json.stamp 404 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_archivist.cm.stamp 405 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_archivist_integration_tests.cm.stamp 406 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_driver.cm.stamp 407 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_metadata.stamp 408 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_stub_inspect_component.cm.stamp 409 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_test/archivist_integration_tests_test_spec.stamp 410 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_validate_manifests_archivist.cm.stamp 411 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_validate_manifests_archivist_integration_tests.cm.stamp 412 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_validate_manifests_driver.cm.stamp 413 | obj/src/diagnostics/archivist/tests/v2/archivist-integration-tests-v2_validate_manifests_stub_inspect_component.cm.stamp 414 | ./archivist_integration_tests 415 | ./stub_inspect_component 416 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 417 | `.replace(/\n/g, ' '); 418 | 419 | const [ 420 | targetBuildDir, packageTarget, componentTargets, 421 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 422 | assert.strictEqual(targetBuildDir, 'src/diagnostics/archivist/tests/v2'); 423 | assert.strictEqual(packageTarget, 'archivist-integration-tests-v2'); 424 | assert.deepStrictEqual(componentTargets, [ 425 | 'archivist.cm', 426 | 'archivist_integration_tests.cm', 427 | 'driver.cm', 428 | 'stub_inspect_component.cm', 429 | 'archivist-integration-tests-v2_test/archivist_integration_tests_test_spec', 430 | ]); 431 | }); 432 | 433 | ////////////////////////////////////////////////////////////////////////////////////////////////// 434 | test('go_test_runner_extractBuildDirPackageTargetAndComponents', () => { 435 | const line = ` 436 | build 437 | obj/src/sys/test_runners/gotests/go-test-runner/meta.far 438 | obj/src/sys/test_runners/gotests/go-test-runner/meta/contents 439 | obj/src/sys/test_runners/gotests/go-test-runner/meta.far.merkle 440 | obj/src/sys/test_runners/gotests/go-test-runner/blobs.json 441 | obj/src/sys/test_runners/gotests/go-test-runner/blobs.manifest 442 | obj/src/sys/test_runners/gotests/go-test-runner/package_manifest.json: 443 | __src_sys_test_runners_gotests_go-test-runner___build_toolchain_fuchsia_arm64__rule 444 | | 445 | ../../build/gn_run_binary.sh 446 | obj/src/sys/test_runners/gotests/go-test-runner_manifest 447 | host_x64/pm 448 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 449 | obj/src/sys/test_runners/gotests/go-test-runner_manifest.stamp 450 | obj/src/sys/test_runners/gotests/go-test-runner_metadata.stamp 451 | obj/src/sys/test_runners/gotests/go_test_runner.stamp 452 | `.replace(/\n/g, ' '); 453 | 454 | const [ 455 | targetBuildDir, packageTarget, componentTargets, 456 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 457 | assert.strictEqual(targetBuildDir, 'src/sys/test_runners/gotests'); 458 | assert.strictEqual(packageTarget, 'go-test-runner'); 459 | assert.deepStrictEqual(componentTargets, ['go_test_runner']); 460 | }); 461 | 462 | ////////////////////////////////////////////////////////////////////////////////////////////////// 463 | test('elf_extractBuildDirPackageTargetAndComponents', () => { 464 | const line = ` 465 | build 466 | obj/src/sys/test_runners/elf/elf-test-runner/meta.far 467 | obj/src/sys/test_runners/elf/elf-test-runner/meta/contents 468 | obj/src/sys/test_runners/elf/elf-test-runner/meta.far.merkle 469 | obj/src/sys/test_runners/elf/elf-test-runner/blobs.json 470 | obj/src/sys/test_runners/elf/elf-test-runner/blobs.manifest 471 | obj/src/sys/test_runners/elf/elf-test-runner/package_manifest.json: 472 | __src_sys_test_runners_elf_elf-test-runner___build_toolchain_fuchsia_arm64__rule 473 | | 474 | ../../build/gn_run_binary.sh 475 | obj/src/sys/test_runners/elf/elf-test-runner_manifest 476 | host_x64/pm 477 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 478 | obj/src/sys/test_runners/elf/elf-test-runner-component.stamp 479 | obj/src/sys/test_runners/elf/elf-test-runner_manifest.stamp 480 | obj/src/sys/test_runners/elf/elf-test-runner_metadata.stamp 481 | `.replace(/\n/g, ' '); 482 | 483 | const [ 484 | targetBuildDir, packageTarget, componentTargets, 485 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 486 | assert.strictEqual(targetBuildDir, 'src/sys/test_runners/elf'); 487 | assert.strictEqual(packageTarget, 'elf-test-runner'); 488 | assert.deepStrictEqual(componentTargets, ['elf-test-runner-component']); 489 | }); 490 | 491 | ////////////////////////////////////////////////////////////////////////////////////////////////// 492 | test('extractBuildDirPackageTargetAndComponents', () => { 493 | const line = ` 494 | build 495 | obj/src/sys/component_manager/component-manager-tests/meta.far 496 | obj/src/sys/component_manager/component-manager-tests/meta/contents 497 | obj/src/sys/component_manager/component-manager-tests/meta.far.merkle 498 | obj/src/sys/component_manager/component-manager-tests/blobs.json 499 | obj/src/sys/component_manager/component-manager-tests/blobs.manifest 500 | obj/src/sys/component_manager/component-manager-tests/package_manifest.json: 501 | __src_sys_component_manager_component-manager-tests___build_toolchain_fuchsia_arm64__rule 502 | | 503 | ../../build/gn_run_binary.sh 504 | obj/src/sys/component_manager/component-manager-tests_manifest 505 | host_x64/pm 506 | obj/examples/components/basic/hello-world.stamp 507 | obj/examples/components/basic/lifecycle-full.stamp 508 | obj/garnet/examples/fidl/echo_server_rust/echo-server-rust-cmp.stamp 509 | obj/src/sys/component_manager/component-manager-boot-env-tests-cmp.stamp 510 | obj/src/sys/component_manager/component-manager-tests-cmp.stamp 511 | obj/src/sys/component_manager/component-manager-tests_manifest.stamp 512 | obj/src/sys/component_manager/component-manager-tests_metadata.stamp 513 | obj/src/sys/component_manager/component-manager-tests_test_component-manager-boot-env-tests-cmp.stamp 514 | obj/src/sys/component_manager/component-manager-tests_test_component-manager-tests-cmp.stamp 515 | obj/src/sys/component_manager/component_manager_tests_invalid_manifest.stamp 516 | ./run_indefinitely 517 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 518 | `.replace(/\n/g, ' '); 519 | 520 | const [ 521 | targetBuildDir, packageTarget, componentTargets, 522 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 523 | assert.strictEqual(targetBuildDir, 'src/sys/component_manager'); 524 | assert.strictEqual(packageTarget, 'component-manager-tests'); 525 | assert.deepStrictEqual(componentTargets, [ 526 | 'component-manager-boot-env-tests-cmp', 527 | 'component-manager-tests-cmp', 528 | 'test_component-manager-boot-env-tests-cmp', 529 | 'test_component-manager-tests-cmp', 530 | 'component_manager_tests_invalid_manifest', 531 | ]); 532 | }); 533 | 534 | ////////////////////////////////////////////////////////////////////////////////////////////////// 535 | test('scenic_extractBuildDirPackageTargetAndComponents', () => { 536 | const line = ` 537 | build 538 | obj/src/ui/scenic/scenic_pkg/meta.far 539 | obj/src/ui/scenic/scenic_pkg/meta/contents 540 | obj/src/ui/scenic/scenic_pkg/meta.far.merkle 541 | obj/src/ui/scenic/scenic_pkg/blobs.json 542 | obj/src/ui/scenic/scenic_pkg/blobs.manifest 543 | obj/src/ui/scenic/scenic_pkg/package_manifest.json: 544 | __src_ui_scenic_scenic_pkg___build_toolchain_fuchsia_arm64__rule 545 | | 546 | ../../build/gn_run_binary.sh 547 | obj/src/ui/scenic/scenic_pkg.manifest 548 | host_x64/pm 549 | obj/build/deprecated_package.stamp 550 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 551 | obj/src/ui/scenic/scenic_pkg.manifest.stamp 552 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_compute_pose_buffer_latching_comp14695981039346656037.spirv.stamp 553 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_flatland_flat_main_frag14695981039346656037.spirv.stamp 554 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_flatland_flat_main_vert14695981039346656037.spirv.stamp 555 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_model_renderer_main_vert12890958529260787213.spirv.stamp 556 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_model_renderer_main_vert15064700897732225279.spirv.stamp 557 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_model_renderer_main_vert4304586084079301274.spirv.stamp 558 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_model_renderer_main_vert7456302057085141907.spirv.stamp 559 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_paper_frag_main_ambient_light_frag4304586084079301274.spirv.stamp 560 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_paper_frag_main_ambient_light_frag7456302057085141907.spirv.stamp 561 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_paper_frag_main_ambient_light_frag9217636760892358205.spirv.stamp 562 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_paper_frag_main_point_light_frag15064700897732225279.spirv.stamp 563 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_paper_vert_main_shadow_volume_extrude_vert15276133142244279294.spirv.stamp 564 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_paper_vert_main_shadow_volume_extrude_vert9217636760892358205.spirv.stamp 565 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_test_main_frag12890958529260787213.spirv.stamp 566 | obj/src/ui/scenic/scenic_pkg.resource.resource.shaders_shaders_test_main_frag4304586084079301274.spirv.stamp 567 | obj/src/ui/scenic/scenic_pkg_metadata.stamp 568 | obj/src/ui/scenic/scenic_pkg_scenic.cmx.stamp 569 | obj/src/ui/scenic/scenic_pkg_scenic.cmx_component_index.stamp 570 | obj/src/ui/scenic/scenic_pkg_validate_manifests_scenic.cmx.stamp 571 | ./scenic 572 | `.replace(/\n/g, ' '); 573 | 574 | const [ 575 | targetBuildDir, packageTarget, componentTargets, 576 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , ['']]; 577 | assert.strictEqual(targetBuildDir, 'src/ui/scenic'); 578 | assert.strictEqual(packageTarget, 'scenic_pkg'); 579 | assert.deepStrictEqual(componentTargets, [ 580 | 'scenic.cmx', 581 | ]); 582 | }); 583 | 584 | ////////////////////////////////////////////////////////////////////////////////////////////////// 585 | test('fonts_extractBuildDirPackageTargetAndComponents', () => { 586 | const line = ` 587 | build 588 | obj/src/fonts/pkg/meta.far 589 | obj/src/fonts/pkg/meta/contents 590 | obj/src/fonts/pkg/meta.far.merkle 591 | obj/src/fonts/pkg/blobs.json 592 | obj/src/fonts/pkg/blobs.manifest 593 | obj/src/fonts/pkg/package_manifest.json: 594 | __src_fonts_pkg___build_toolchain_fuchsia_arm64__rule 595 | | 596 | ../../build/gn_run_binary.sh 597 | obj/src/fonts/pkg.manifest 598 | host_x64/pm 599 | obj/build/deprecated_package.stamp 600 | ./font_provider 601 | obj/src/fonts/pkg.manifest.stamp 602 | obj/src/fonts/pkg_fonts.cm.stamp 603 | obj/src/fonts/pkg_fonts.cmx.stamp 604 | obj/src/fonts/pkg_fonts.cmx_component_index.stamp 605 | obj/src/fonts/pkg_fonts_for_downstream_tests.cmx.stamp 606 | obj/src/fonts/pkg_fonts_for_downstream_tests.cmx_component_index.stamp 607 | obj/src/fonts/pkg_metadata.stamp 608 | obj/src/fonts/pkg_validate_manifests_fonts.cm.stamp 609 | obj/src/fonts/pkg_validate_manifests_fonts.cmx.stamp 610 | obj/src/fonts/pkg_validate_manifests_fonts_for_downstream_tests.cmx.stamp 611 | host_x64/obj/src/sys/pkg/bin/pm/pm_bin.stamp 612 | `.replace(/\n/g, ' '); 613 | 614 | const [ 615 | targetBuildDir, packageTarget, componentTargets, 616 | ] = Provider.extractBuildDirPackageTargetAndComponents(line) ?? [, , []]; 617 | assert.strictEqual(targetBuildDir, 'src/fonts'); 618 | assert.strictEqual(packageTarget, 'pkg'); 619 | assert.deepStrictEqual(componentTargets, [ 620 | 'fonts.cm', 621 | 'fonts.cmx', 622 | 'fonts_for_downstream_tests.cmx', 623 | ]); 624 | }); 625 | 626 | ////////////////////////////////////////////////////////////////////////////////////////////////// 627 | test('fonts_extractManifestPathAndCmlComponent', () => { 628 | const line = ` 629 | command 630 | = 631 | 632 | ../../build/rbe/output-scanner.sh 633 | --label 634 | //src/fonts$:font_provider_cm_manifest_compile\(//build/toolchain/fuchsia$:x64\) 635 | obj/src/fonts/cml/font_provider_cm_manifest_compile/fonts.cm 636 | -- 637 | ../../build/gn_run_binary.sh 638 | ../../prebuilt/third_party/clang/linux-x64/bin 639 | host_x64/cmc 640 | compile 641 | ../../src/fonts/meta/fonts.cml 642 | --output 643 | obj/src/fonts/cml/font_provider_cm_manifest_compile/fonts.cm 644 | --depfile 645 | obj/src/fonts/font_provider_cm_manifest_compile.d 646 | --config-package-path 647 | meta/fonts.cvf 648 | --includeroot 649 | ../../ 650 | --includepath 651 | ../../sdk/lib/ 652 | --features 653 | hub 654 | `.replace(/\n/g, ' '); 655 | 656 | const [ 657 | manifestSourcePath, componentName, componentTargetPath 658 | ] = Provider.extractManifestPathAndCmlComponent(line) ?? []; 659 | assert.strictEqual(manifestSourcePath, 'src/fonts/meta/fonts.cml'); 660 | assert.strictEqual(componentName, 'fonts'); 661 | assert.strictEqual(componentTargetPath, 'src/fonts:font_provider_cm'); 662 | }); 663 | 664 | ////////////////////////////////////////////////////////////////////////////////////////////////// 665 | test('elf_extractManifestPathAndCmlComponent', () => { 666 | const line = ` 667 | command 668 | = 669 | 670 | ../../build/rbe/output-scanner.sh 671 | --label 672 | //src/sys/test_runners/elf$:elf-test-runner-component_manifest_compile\(//build/toolchain/fuchsia$:x64\) 673 | obj/src/sys/test_runners/elf/cml/elf-test-runner-component_manifest_compile/elf-test-runner.cm 674 | -- 675 | ../../build/gn_run_binary.sh 676 | ../../prebuilt/third_party/clang/linux-x64/bin 677 | host_x64/cmc 678 | compile 679 | ../../src/sys/test_runners/elf/meta/elf_test_runner.cml 680 | --output 681 | obj/src/sys/test_runners/elf/cml/elf-test-runner-component_manifest_compile/elf-test-runner.cm 682 | --depfile 683 | obj/src/sys/test_runners/elf/elf-test-runner-component_manifest_compile.d 684 | --config-package-path 685 | meta/elf-test-runner.cvf 686 | --includeroot 687 | ../../ 688 | --includepath 689 | ../../sdk/lib/ 690 | --features 691 | hub 692 | `.replace(/\n/g, ' '); 693 | 694 | const [ 695 | manifestSourcePath, componentName, componentTargetPath 696 | ] = Provider.extractManifestPathAndCmlComponent(line) ?? []; 697 | assert.strictEqual(manifestSourcePath, 'src/sys/test_runners/elf/meta/elf_test_runner.cml'); 698 | assert.strictEqual(componentName, 'elf-test-runner'); 699 | assert.strictEqual(componentTargetPath, 'src/sys/test_runners/elf:elf-test-runner-component'); 700 | }); 701 | 702 | ////////////////////////////////////////////////////////////////////////////////////////////////// 703 | test('extractPackage', () => { 704 | const line = ` 705 | build 706 | obj/src/sys/test_manager/test_manager_pkg/meta.far 707 | obj/src/sys/test_manager/test_manager_pkg/meta.far.merkle 708 | obj/src/sys/test_manager/test_manager_pkg/blobs.json 709 | obj/src/sys/test_manager/test_manager_pkg/blobs.manifest 710 | obj/src/sys/test_manager/test_manager_pkg/package_manifest.json: 711 | __src_sys_test_manager_test_manager_pkg.pm___build_toolchain_fuchsia_x64__rule 712 | | 713 | ../../build/rbe/output-scanner.sh 714 | obj/src/sys/test_manager/test_manager_pkg_manifest 715 | host_x64/package-tool 716 | ../../build/gn_run_binary.sh 717 | obj/src/diagnostics/archivist/archivist-for-embedding-v2.stamp 718 | obj/src/storage/memfs/memfs_component.stamp 719 | obj/src/sys/early_boot_instrumentation/early-boot-instrumentation.stamp 720 | host_x64/obj/src/sys/pkg/bin/package-tool/package-tool.stamp 721 | obj/src/sys/test_manager/test_manager_cmp.stamp 722 | obj/src/sys/test_manager/test_manager_pkg.pm_metadata.stamp 723 | obj/src/sys/test_manager/test_manager_pkg.verify.stamp 724 | obj/src/sys/test_manager/test_manager_pkg_manifest.stamp 725 | obj/src/sys/test_manager/cmx_runner/cmx_runner.stamp 726 | obj/src/sys/test_manager/debug_data/debug_data_rust.stamp 727 | obj/src/sys/test_manager/debug_data_processor/debug_data_processor.stamp 728 | `.replace(/\n/g, ' '); 729 | 730 | const [ 731 | packageName, packageTargetPath 732 | ] = Provider.extractPackage(line) ?? []; 733 | assert.strictEqual(packageName, 'archivist-for-embedding-v2'); 734 | assert.strictEqual(packageTargetPath, 'src/sys/test_manager:test_manager_pkg'); 735 | }); 736 | 737 | ////////////////////////////////////////////////////////////////////////////////////////////////// 738 | test('provideDocumentLinks matches .cm files (compiled cml)', async () => { 739 | const provider = new Provider(baseUri, buildDir); 740 | const docWithComponentUrl = await vscode.workspace.openTextDocument({ 741 | content: ` 742 | componentUrl: "fuchsia-pkg://fuchsia.com/some-package?1a2b3c4d5e6f#meta/some-component.cm" 743 | ` 744 | }); 745 | provider.addLink('some-package', 'some-component', 'src/some/path.cml_or_cmx'); 746 | const links = provider.provideDocumentLinks( 747 | docWithComponentUrl, new vscode.CancellationTokenSource().token); 748 | assert.deepStrictEqual(links?.map(link => link.target?.path), [ 749 | '/fuchsia/src/some/path.cml_or_cmx', 750 | ]); 751 | }); 752 | 753 | ////////////////////////////////////////////////////////////////////////////////////////////////// 754 | test('provideDocumentLinks matches .cmx files, and fxrev and fxbug IDs', async () => { 755 | const provider = new Provider(baseUri, buildDir); 756 | const docWithComponentUrl = await vscode.workspace.openTextDocument({ 757 | content: ` 758 | componentUrl: "fuchsia-pkg://fuchsia.com/some-package?1a2b3c4d5e6f#meta/some-component.cmx" 759 | // ISSUE(fxrev.dev/1012345): 760 | // ISSUE(http://fxrev.dev/2012345): 761 | // ISSUE(https://fxrev.dev/3012345): 762 | // ISSUE(fxr/4012345): 763 | // ISSUE(fxbug.dev/5012345): 764 | // ISSUE(http://fxbug.dev/6012345): 765 | // ISSUE(https://fxbug.dev/7012345): 766 | // ISSUE(fxb/8012345): 767 | ` 768 | }); 769 | provider.addLink('some-package', 'some-component', 'src/some/path.cml_or_cmx'); 770 | const links = provider.provideDocumentLinks( 771 | docWithComponentUrl, new vscode.CancellationTokenSource().token); 772 | assert.deepStrictEqual(links?.map(link => link.target?.toString()), [ 773 | 'file:///fuchsia/src/some/path.cml_or_cmx', 774 | 'https://fxrev.dev/1012345', 775 | 'https://fxrev.dev/2012345', 776 | 'https://fxrev.dev/3012345', 777 | 'https://fxrev.dev/4012345', 778 | 'https://fxbug.dev/5012345', 779 | 'https://fxbug.dev/6012345', 780 | 'https://fxbug.dev/7012345', 781 | 'https://fxbug.dev/8012345', 782 | ]); 783 | }); 784 | 785 | ////////////////////////////////////////////////////////////////////////////////////////////////// 786 | test('provideTerminalLinks matches .cmx files', async () => { 787 | const provider = new Provider(baseUri, buildDir); 788 | const context = { 789 | line: ` componentUrl: "fuchsia-pkg://fuchsia.com/some-package#meta/some-component.cmx"` 790 | }; 791 | provider.addLink('some-package', 'some-component', 'src/some/path.cml_or_cmx'); 792 | const links = provider.provideTerminalLinks( 793 | context, new vscode.CancellationTokenSource().token); 794 | assert.strictEqual((links ?? [])[0].startIndex, 19); 795 | }); 796 | 797 | // TODO(#13): Re-enable this test 798 | // ////////////////////////////////////////////////////////////////////////////////////////////////// 799 | // test('finds references to a manifest', async () => { 800 | // const provider = new Provider(baseUri, buildDir); 801 | // const referencedManifestDoc = await vscode.languages.setTextDocumentLanguage( 802 | // await vscode.workspace.openTextDocument({ 803 | // content: `{ 804 | // program: { 805 | // binary: "bin/some_component_exe", 806 | // }, 807 | // } 808 | // ` 809 | // }), 810 | // 'untitled-fuchsia-manifest', 811 | // ); 812 | 813 | // const packageName = 'some-package'; 814 | // const componentName = 'some-component'; 815 | // const componentUrl = `fuchsia-pkg://fuchsia.com/${packageName}#meta/${componentName}.cm`; 816 | 817 | // const docWithComponentUrl = await vscode.workspace.openTextDocument({ 818 | // content: ` 819 | // componentUrl: "fuchsia-pkg://fuchsia.com/${packageName}?1a2b3c4d5e6f#meta/${componentName}.cmx" 820 | // ` 821 | // }); 822 | 823 | // provider.addLink(packageName, componentName, referencedManifestDoc.uri.fsPath); 824 | 825 | // provider.addReference( 826 | // packageName, 827 | // componentName, 828 | // componentUrl, 829 | // vscode.Uri.file('src/some/path_to_some_referrer.txt'), 830 | // 10, 831 | // 5, 832 | // ); 833 | 834 | // provider.addReference( 835 | // packageName, 836 | // componentName, 837 | // componentUrl, 838 | // vscode.Uri.file('src/some/path_to_another_referrer.txt'), 839 | // 10, 840 | // 5, 841 | // ); 842 | 843 | // const references = provider.provideReferences( 844 | // referencedManifestDoc, 845 | // new vscode.Position(0, 0), 846 | // { includeDeclaration: false }, 847 | // new vscode.CancellationTokenSource().token, 848 | // ); 849 | // assert.deepStrictEqual(references?.map(location => location.uri.path), [ 850 | // '/src/some/path_to_some_referrer.txt', 851 | // '/src/some/path_to_another_referrer.txt', 852 | // ]); 853 | // }); 854 | }); 855 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import * as path from 'path'; 16 | import * as Mocha from 'mocha'; 17 | import * as glob from 'glob'; 18 | 19 | export function run(): Promise { 20 | // Create the mocha test 21 | const mocha = new Mocha({ 22 | ui: 'tdd', 23 | color: true 24 | }); 25 | 26 | const testsRoot = path.resolve(__dirname, '..'); 27 | 28 | return new Promise((c, e) => { 29 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 30 | if (err) { 31 | return e(err); 32 | } 33 | 34 | // Add files to the test suite 35 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 36 | 37 | try { 38 | // Run the mocha test 39 | mocha.run(failures => { 40 | if (failures > 0) { 41 | e(new Error(`${failures} tests failed.`)); 42 | } else { 43 | c(); 44 | } 45 | }); 46 | } catch (err) { 47 | console.error(err); 48 | e(err); 49 | } 50 | }); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | --------------------------------------------------------------------------------