Last active
April 20, 2020 12:31
-
-
Save qboileau/dabd03c7a3758e908253e5d06c0162e1 to your computer and use it in GitHub Desktop.
Local copy of rust script example from Mahmoud Al-Qudsi blog post https://neosmart.net/blog/self-compiling-rust-code/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| #![allow()] /* | |
| # rust self-compiler by Mahmoud Al-Qudsi, Copyright NeoSmart Technologies 2020 | |
| # See <https://neosmart.net/blog/self-compiling-rust-code/> for info & updates. | |
| # | |
| # This code is freely released to the public domain. In case a public domain | |
| # license is insufficient for your legal department, this code is also licensed | |
| # under the MIT license. | |
| # Get an output path that is derived from the complete path to this self script. | |
| # - `realpath` makes sure if you have two separate `script.rs` files in two | |
| # different directories, they get mapped to different binaries. | |
| # - `which` makes that work even if you store this script in $PATH and execute | |
| # it by its filename alone. | |
| # - `cut` is used to print only the hash and not the filename, which `md5sum` | |
| # always includes in its output. | |
| OUT=/tmp/$(printf "%s" $(realpath $(which "$0")) | md5sum | cut -d' ' -f1) | |
| # Calculate hash of the current contents of the script, so we can avoid | |
| # recompiling if it hasn't changed. | |
| MD5=$(md5sum "$0" | cut -d' ' -f1) | |
| # Check if we have a previously compiled output for this exact source code. | |
| if !(test -f "${OUT}.md5" && test "${MD5}" = "$(cat ${OUT}.md5)"); then | |
| # The script has been modified or is otherwise not cached. | |
| # Check if the script already contains an `fn main()` entry point. | |
| if grep -Eq '^\s*(\[.*?\])*\s*fn\s*main\b*' "$0"; then | |
| # Compile the input script as-is to the previously determined location. | |
| rustc "$0" -o ${OUT} | |
| # Save rustc's exit code so we can compare against it later. | |
| RUSTC_STATUS=$? | |
| else | |
| # The script does not contain an `fn main()` entry point, so add one. | |
| # We don't use `printf 'fn main() { %s }' because the shebang must | |
| # come at the beginning of the line, and we don't use `tail` to skip | |
| # it because that would result in incorrect line numbers in any errors | |
| # reported by rustc, instead we just comment out the shebang but leave | |
| # it on the same line as `fn main() {`. | |
| printf "fn main() {//%s\n}" "$(cat $0)" | rustc - -o ${OUT} | |
| # Save rustc's exit code so we can compare against it later. | |
| RUSTC_STATUS=$? | |
| fi | |
| # Check if we compiled the script OK, or exit bubbling up the return code. | |
| if test "${RUSTC_STATUS}" -ne 0; then | |
| exit ${RUSTC_STATUS} | |
| fi | |
| # Save the MD5 of the current version of the script so we can compare | |
| # against it next time. | |
| printf "%s" ${MD5} > ${OUT}.md5 | |
| fi | |
| # Execute the compiled output. This also ends execution of the shell script, | |
| # as it actually replaces its process with ours; see exec(3) for more on this. | |
| exec ${OUT} $@ | |
| # At this point, it's OK to write raw rust code as the shell interpreter | |
| # never gets this far. But we're actually still in the rust comment we opened | |
| # on line 2, so close that: */ | |
| fn main() { | |
| let name = std::env::args().skip(1).next().unwrap_or("world".into()); | |
| println!("Hello, {}!", name); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment