1- //! Makes `rust-embed` see a changed asset tree, records the commit being
2- //! built, and installs the git hooks .
1+ //! Makes `rust-embed` see a changed asset tree and records the commit being
2+ //! built.
33//!
44//! The derive expands to one `include_bytes!` per file, and rustc records
55//! those in dep-info, so *editing* an embedded asset already rebuilds. Files
@@ -19,7 +19,6 @@ use std::process::Command;
1919fn main ( ) {
2020 println ! ( "cargo:rerun-if-changed=web" ) ;
2121 set_build_commit ( ) ;
22- install_git_hooks ( ) ;
2322}
2423
2524/// The released binary must identify the source it was built from even when
@@ -102,99 +101,3 @@ fn watch_git_head(root: &std::path::Path) {
102101fn git_path ( root : & std:: path:: Path , name : & str ) -> Option < std:: path:: PathBuf > {
103102 git_output ( root, & [ "rev-parse" , "--git-path" , name] ) . map ( |path| root. join ( path. trim ( ) ) )
104103}
105-
106- /// Installs a wrapper for each `scripts/git-*.sh` in the hooks directory of a
107- /// checkout, unless they are already in effect.
108- ///
109- /// A hook nobody installed is a hook nobody runs, and the first person to find
110- /// that out is whoever reads a commit message the rules would have caught. A
111- /// build is the one step every contributor takes, so it is where installation
112- /// belongs; `make hooks` stays for anyone who wants it on its own. `cargo
113- /// check`, clippy and rust-analyzer share this script's run, so whichever comes
114- /// first in a fresh clone installs.
115- ///
116- /// Never fails the build. A checkout can be read-only, a hooks directory can
117- /// belong to somebody else's tooling, and a Windows machine may have no `sh`.
118- /// None of that is a reason to refuse to compile, so a failure is a warning
119- /// and the build goes on.
120- fn install_git_hooks ( ) {
121- let root = std:: env:: var_os ( "CARGO_MANIFEST_DIR" ) . map ( std:: path:: PathBuf :: from) ;
122- let Some ( root) = root else { return } ;
123-
124- // A worktree has a `.git` file rather than a directory, and both mean the
125- // same thing here: this is a checkout, so it has hooks worth installing. A
126- // packaged crate has neither.
127- if !root. join ( ".git" ) . exists ( ) {
128- return ;
129- }
130-
131- // Watched so that a changed installer, or a deleted hook, reruns this.
132- // Without the second Cargo would keep the build script fresh and a removed
133- // hook would stay removed until something else forced a rerun. The first
134- // build after a fresh clone pays one extra rebuild, because installing the
135- // hooks is itself a change to the directory being watched.
136- //
137- // Not the whole of scripts/, though adding a hook there is the one change
138- // this does not notice. That directory holds every gate script in the
139- // repository and each edit to one would rebuild the binary; `make hooks`
140- // covers the rare case at no standing cost.
141- //
142- // A checkout without the installer has nothing to run, and a watch on a
143- // path that does not exist would rerun this script on every build.
144- if !root. join ( "scripts/install-git-hooks.sh" ) . is_file ( ) {
145- return ;
146- }
147- println ! ( "cargo:rerun-if-changed=scripts/install-git-hooks.sh" ) ;
148- run_hook_installer ( & root) ;
149-
150- // Asked of git rather than assumed to be `.git/hooks`: a linked worktree
151- // keeps its hooks with the common directory, and core.hooksPath moves them
152- // anywhere. After the install rather than before, because a clone made with
153- // an empty template has no hooks directory until the installer makes one,
154- // and a directory that was not there to watch is never watched.
155- if let Some ( hooks) = git_path ( & root, "hooks" )
156- && hooks. is_dir ( )
157- {
158- println ! ( "cargo:rerun-if-changed={}" , hooks. display( ) ) ;
159- }
160- }
161-
162- fn run_hook_installer ( root : & std:: path:: Path ) {
163- let installer = root. join ( "scripts/install-git-hooks.sh" ) ;
164- let run = |args : & [ & str ] | {
165- Command :: new ( "sh" )
166- . arg ( & installer)
167- . args ( args)
168- . current_dir ( root)
169- . output ( )
170- } ;
171-
172- // Installed already, which is every build after the first: the check writes
173- // nothing, so it leaves the watched directory alone and says nothing. The
174- // installer stays the one judge of what installed means.
175- //
176- // No `sh` at all is the other quiet case, in practice a Git for Windows
177- // install that did not put its shell on PATH. The release job builds a
178- // Windows target on every push, and a warning nobody can act on teaches
179- // people to skim them.
180- match run ( & [ "--check" ] ) {
181- Ok ( output) if output. status . success ( ) => return ,
182- Err ( error) if error. kind ( ) == std:: io:: ErrorKind :: NotFound => return ,
183- _ => { }
184- }
185-
186- // Cargo swallows a build script's stdout, which is right for the OK and
187- // HOOK lines. The installer writes what needs a person to stderr instead: a
188- // hook it refused to overwrite, a core.hooksPath shared with other
189- // repositories, and how to fix each. That reaches the terminal line for
190- // line or nothing does.
191- let reason = match run ( & [ ] ) {
192- Ok ( output) if output. status . success ( ) => return ,
193- Ok ( output) => String :: from_utf8_lossy ( & output. stderr ) . into_owned ( ) ,
194- Err ( error) => error. to_string ( ) ,
195- } ;
196- for line in reason. lines ( ) . filter ( |line| !line. trim ( ) . is_empty ( ) ) {
197- println ! ( "cargo::warning=git hooks: {line}" ) ;
198- }
199- println ! ( "cargo::warning=git hooks are not all in effect; run make hooks" ) ;
200- }
0 commit comments