blob: e55d78f09e476c2ef4abe8f6cc999bd358c51214 [file] [log] [blame]
Ben Claytone359a932024-03-14 09:22:24 +00001// Copyright 2024 The Dawn & Tint Authors
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Package install installs the tintd vscode extension for local development
Ben Claytone359a932024-03-14 09:22:24 +000029package install
30
31import (
32 "bytes"
33 "context"
Ryan Harrisonae947212025-07-31 07:27:08 -070034 "dawn.googlesource.com/dawn/tools/src/oswrapper"
Ben Claytone359a932024-03-14 09:22:24 +000035 "encoding/json"
36 "flag"
37 "fmt"
Ben Claytone359a932024-03-14 09:22:24 +000038 "os/exec"
39 "path/filepath"
40
41 "dawn.googlesource.com/dawn/tools/src/cmd/tintd/common"
42 "dawn.googlesource.com/dawn/tools/src/fileutils"
43)
44
45func init() {
46 common.Register(&Cmd{})
47}
48
49type Cmd struct {
50 flags struct {
Ben Clayton2f215d12024-03-23 10:18:00 +000051 symlink bool
Ben Claytone359a932024-03-14 09:22:24 +000052 buildDir string
53 npmPath string
54 }
55}
56
57func (Cmd) Name() string {
58 return "install"
59}
60
61func (Cmd) Desc() string {
Ben Clayton2f215d12024-03-23 10:18:00 +000062 return `install installs the tintd vscode extension for local development`
Ben Claytone359a932024-03-14 09:22:24 +000063}
64
Brian Sheedy5c35c852025-05-13 06:38:33 -070065func (c *Cmd) RegisterFlags(ctx context.Context, cfg *common.Config) ([]string, error) {
66 dawnRoot := fileutils.DawnRoot(cfg.OsWrapper)
Ben Claytone359a932024-03-14 09:22:24 +000067 npmPath, _ := exec.LookPath("npm")
Ben Clayton2f215d12024-03-23 10:18:00 +000068 flag.BoolVar(&c.flags.symlink, "symlink", false, "create a symlink from the vscode extension directory to the build directory")
Ben Claytone359a932024-03-14 09:22:24 +000069 flag.StringVar(&c.flags.buildDir, "build", filepath.Join(dawnRoot, "out", "active"), "the output build directory")
70 flag.StringVar(&c.flags.npmPath, "npm", npmPath, "path to npm")
71
72 return nil, nil
73}
74
Brian Sheedy5c35c852025-05-13 06:38:33 -070075// TODO(crbug.com/416755658): Add unittest coverage once exec is handled via
76// dependency injection.
77func (c Cmd) Run(ctx context.Context, cfg *common.Config) error {
Ryan Harrisonae947212025-07-31 07:27:08 -070078 pkgDir := c.findPackage(cfg.OsWrapper)
Ben Claytone359a932024-03-14 09:22:24 +000079 if pkgDir == "" {
80 return fmt.Errorf("could not find extension package directory at '%v'", c.flags.buildDir)
81 }
82
Ryan Harrisonae947212025-07-31 07:27:08 -070083 if !fileutils.IsExe(c.flags.npmPath, cfg.OsWrapper) {
Ben Claytone359a932024-03-14 09:22:24 +000084 return fmt.Errorf("could not find npm")
85 }
86
87 // Build the package
88 npmCmd := exec.Command(c.flags.npmPath, "install")
89 npmCmd.Dir = pkgDir
90 if out, err := npmCmd.CombinedOutput(); err != nil {
91 return fmt.Errorf("npm install failed:\n%v\n%v", err, string(out))
92 }
93
94 // Load the package to get the name and version
95 pkg := struct {
96 Name string
97 Version string
98 }{}
99 packageJSONPath := filepath.Join(pkgDir, "package.json")
Brian Sheedy5c35c852025-05-13 06:38:33 -0700100 packageJSON, err := cfg.OsWrapper.ReadFile(packageJSONPath)
Ben Claytone359a932024-03-14 09:22:24 +0000101 if err != nil {
102 return fmt.Errorf("could not open '%v'", packageJSONPath)
103 }
104 if err := json.NewDecoder(bytes.NewReader(packageJSON)).Decode(&pkg); err != nil {
105 return fmt.Errorf("could not parse '%v': %v", packageJSONPath, err)
106 }
107
Brian Sheedy5c35c852025-05-13 06:38:33 -0700108 home, err := cfg.OsWrapper.UserHomeDir()
Ben Claytone359a932024-03-14 09:22:24 +0000109 if err != nil {
110 return fmt.Errorf("failed to obtain home directory: %w", err)
111 }
112 vscodeBaseExtsDir := filepath.Join(home, ".vscode", "extensions")
Ryan Harrisonae947212025-07-31 07:27:08 -0700113 if !fileutils.IsDir(vscodeBaseExtsDir, cfg.OsWrapper) {
Ben Claytone359a932024-03-14 09:22:24 +0000114 return fmt.Errorf("vscode extensions directory not found at '%v'", vscodeBaseExtsDir)
115 }
116
117 vscodeTintdDir := filepath.Join(vscodeBaseExtsDir, fmt.Sprintf("google.%v-%v", pkg.Name, pkg.Version))
Brian Sheedy5c35c852025-05-13 06:38:33 -0700118 cfg.OsWrapper.RemoveAll(vscodeTintdDir)
Ben Claytone359a932024-03-14 09:22:24 +0000119
Ben Clayton2f215d12024-03-23 10:18:00 +0000120 if c.flags.symlink {
121 // Symlink the vscode extensions directory to the build directory
Brian Sheedy5c35c852025-05-13 06:38:33 -0700122 if err := cfg.OsWrapper.Symlink(pkgDir, vscodeTintdDir); err != nil {
Ben Clayton2f215d12024-03-23 10:18:00 +0000123 return fmt.Errorf("failed to create symlink '%v' <- '%v': %w", pkgDir, vscodeTintdDir, err)
124 }
125 } else {
126 // Copy the build directory to vscode extensions directory
Ryan Harrisonae947212025-07-31 07:27:08 -0700127 if err := fileutils.CopyDir(vscodeTintdDir, pkgDir, cfg.OsWrapper); err != nil {
Ben Clayton2f215d12024-03-23 10:18:00 +0000128 return fmt.Errorf("failed to copy '%v' to '%v': %w", pkgDir, vscodeTintdDir, err)
129 }
Ben Claytone359a932024-03-14 09:22:24 +0000130 }
131
132 return nil
133}
134
Brian Sheedy5c35c852025-05-13 06:38:33 -0700135// TODO(crbug.com/344014313): Add unittest coverage.
Ben Claytone359a932024-03-14 09:22:24 +0000136// findPackage looks for and returns the tintd package directory. Returns an empty string if not found.
Ryan Harrisonae947212025-07-31 07:27:08 -0700137func (c Cmd) findPackage(fsReader oswrapper.FilesystemReader) string {
Ben Claytone359a932024-03-14 09:22:24 +0000138 searchPaths := []string{
139 filepath.Join(c.flags.buildDir, "gen/vscode"),
140 c.flags.buildDir,
141 }
142 files := []string{"tintd", "package.json"}
143
144nextDir:
145 for _, dir := range searchPaths {
146 for _, file := range files {
Ryan Harrisonae947212025-07-31 07:27:08 -0700147 if !fileutils.IsFile(filepath.Join(dir, file), fsReader) {
Ben Claytone359a932024-03-14 09:22:24 +0000148 continue nextDir
149 }
150 }
151 return dir
152 }
153
154 return "" // Not found
155}