├── .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 | [](https://marketplace.visualstudio.com/items?itemName=RichKadel.fuchsiaware)
5 | [](https://github.com/google/fuchsiaware/blob/main/LICENSE)
6 | [](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 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------