From 82b3681a742b4ee131254594b1f48979aeaf5a5c Mon Sep 17 00:00:00 2001 From: James Date: Mon, 6 Aug 2018 20:51:59 +1000 Subject: [PATCH] Add support for shortcuts on Windows --- Cargo.lock | 200 +++++++++++++++++++++++++++++++ Cargo.toml | 2 + build.rs | 21 ++++ src/config.rs | 10 ++ src/installer.rs | 20 +++- src/main.rs | 1 + src/native/interop.cpp | 89 ++++++++++++++ src/native/interop.h | 6 + src/native/mod.rs | 80 +++++++++++++ src/rest.rs | 18 ++- src/tasks/download_pkg.rs | 13 +- src/tasks/install.rs | 39 +++--- src/tasks/install_dir.rs | 3 +- src/tasks/install_pkg.rs | 61 ++++++---- src/tasks/install_shortcuts.rs | 98 +++++++++++++++ src/tasks/mod.rs | 80 +++++++++++-- src/tasks/resolver.rs | 3 +- src/tasks/save_database.rs | 3 +- src/tasks/save_executable.rs | 3 +- src/tasks/uninstall.rs | 20 ++-- src/tasks/uninstall_pkg.rs | 19 ++- src/tasks/uninstall_shortcuts.rs | 100 ++++++++++++++++ 22 files changed, 823 insertions(+), 66 deletions(-) create mode 100644 src/native/interop.cpp create mode 100644 src/native/interop.h create mode 100644 src/native/mod.rs create mode 100644 src/tasks/install_shortcuts.rs create mode 100644 src/tasks/uninstall_shortcuts.rs diff --git a/Cargo.lock b/Cargo.lock index b59b75b..40355a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,14 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "aster" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.11" @@ -46,6 +54,31 @@ dependencies = [ "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "bindgen" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bitflags" version = "0.9.1" @@ -98,6 +131,14 @@ name = "cc" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "cexpr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cfg-if" version = "0.1.4" @@ -113,6 +154,17 @@ dependencies = [ "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clang-sys" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "clap" version = "2.32.0" @@ -204,6 +256,15 @@ dependencies = [ "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "env_logger" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fern" version = "0.5.6" @@ -282,6 +343,11 @@ name = "gcc" version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "glob" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "httparse" version = "1.3.2" @@ -403,10 +469,22 @@ dependencies = [ "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "libloading" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "liftinstall" version = "0.1.0" dependencies = [ + "bindgen 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -454,6 +532,14 @@ name = "matches" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "memchr" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "memchr" version = "2.0.1" @@ -600,6 +686,14 @@ name = "nodrop" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-integer" version = "0.1.39" @@ -652,6 +746,11 @@ dependencies = [ "vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "percent-encoding" version = "1.0.1" @@ -710,6 +809,26 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quasi" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quasi_codegen" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.6.5" @@ -811,6 +930,11 @@ dependencies = [ "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "safemem" version = "0.2.0" @@ -948,6 +1072,48 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syntex" +version = "0.58.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syntex_errors" +version = "0.58.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syntex_pos" +version = "0.58.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syntex_syntax" +version = "0.58.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "take" version = "0.1.0" @@ -973,6 +1139,15 @@ dependencies = [ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "term" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termion" version = "1.5.1" @@ -1233,6 +1408,11 @@ name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.1.0" @@ -1422,8 +1602,11 @@ dependencies = [ "checksum aho-corasick 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c6d463cbe7ed28720b5b489e7c083eeb8f90d08be2a0d6bb9e1ffea9ce1afa" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9" +"checksum bindgen 0.26.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c57d6c0f6e31f8dcf4d12720a3c2a9ffb70638772a5784976cf4fce52145f22a" +"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" @@ -1432,8 +1615,10 @@ dependencies = [ "checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" "checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b" "checksum cc 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "2119ea4867bd2b8ed3aecab467709720b2d55b1bcfe09f772fd68066eaf15275" +"checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c" "checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" "checksum chrono 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e48d85528df61dc964aa43c5f6ca681a19cfa74939b2348d204bd08a981f2fb0" +"checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" @@ -1444,6 +1629,7 @@ dependencies = [ "checksum dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37a76dd8b997af7107d0bb69d43903cf37153a18266f8b3fdb9911f28efb5444" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" "checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d" +"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" "checksum fern 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "57915fe00a83af935983eb2d00b0ecc62419c4741b28c207ecbf98fd4a1b94c8" "checksum filetime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da4b9849e77b13195302c174324b5ba73eec9b236b24c221a61000daefb95c5f" "checksum flate2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37847f133aae7acf82bb9577ccd8bda241df836787642654286e79679826a54b" @@ -1455,6 +1641,7 @@ dependencies = [ "checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" +"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b6288d7db100340ca12873fd4d08ad1b8f206a9457798dfb17c018a33fee540" "checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" "checksum hyper-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffb1bd5e518d3065840ab315dbbf44e4420e5f7d80e2cb93fa6ffffc50522378" @@ -1469,9 +1656,11 @@ dependencies = [ "checksum libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122" "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98" +"checksum libloading 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fd38073de8f7965d0c17d30546d4bb6da311ab428d1c7a3fc71dff7f9d4979b9" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2" "checksum matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "835511bab37c34c47da5cb44844bea2cfde0236db0b506f90ea4224482c9774a" +"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" @@ -1487,12 +1676,14 @@ dependencies = [ "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e752e3c216bc8a491c5b59fa46da10f1379ae450b19ac688e07f4bb55042e98" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" "checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" "checksum openssl-sys 0.9.35 (registry+https://github.com/rust-lang/crates.io-index)" = "912f301a749394e1025d9dcddef6106ddee9252620e6d0a0e5f8d0681de9b129" +"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" "checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" @@ -1501,6 +1692,8 @@ dependencies = [ "checksum pkg-config 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "104630aa1c83213cbc76db0703630fcb0421dac3585063be4ce9a8a2feeaa745" "checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6" +"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" +"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" "checksum quote 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3372dc35766b36a99ce2352bd1b6ea0137c38d215cc0c8780bf6de6df7842ba9" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" @@ -1511,6 +1704,7 @@ dependencies = [ "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum reqwest 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e237e32c3bfa55c95e29af872c8f481471d70b8a5ec15d85f4d274ffd92dd9" +"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" "checksum schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1fabf2a7b6483a141426e1afd09ad543520a77ac49bd03c286e7696ccfd77f" @@ -1530,9 +1724,14 @@ dependencies = [ "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.14.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e13df71f29f9440b50261a5882c86eac334f1badb3134ec26f0de2f1418e44" +"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e" +"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c" +"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047" +"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791" "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum tar 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "e8f41ca4a5689f06998f0247fcb60da6c760f1950cc9df2a10d71575ad0b062a" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" @@ -1559,6 +1758,7 @@ dependencies = [ "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" diff --git a/Cargo.toml b/Cargo.toml index a1bbf24..eccd58d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,3 +51,5 @@ nfd = "0.0.4" [target.'cfg(windows)'.build-dependencies] winres = "0.1" +bindgen = "0.26.3" +cc = "1.0" diff --git a/build.rs b/build.rs index e63ec4d..231a70e 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,12 @@ extern crate walkdir; #[cfg(windows)] extern crate winres; +#[cfg(windows)] +extern crate bindgen; + +#[cfg(windows)] +extern crate cc; + use walkdir::WalkDir; use std::env; @@ -24,6 +30,21 @@ fn handle_binary() { let mut res = winres::WindowsResource::new(); res.set_icon("static/favicon.ico"); res.compile().expect("Failed to generate metadata"); + + let bindings = bindgen::Builder::default() + .header("src/native/interop.h") + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("interop.rs")) + .expect("Couldn't write bindings!"); + + cc::Build::new() + .cpp(true) + .file("src/native/interop.cpp") + .compile("interop"); } #[cfg(not(windows))] diff --git a/src/config.rs b/src/config.rs index b5eda7d..ecff603 100644 --- a/src/config.rs +++ b/src/config.rs @@ -19,6 +19,14 @@ pub struct PackageSource { pub config: toml::Value, } +/// Describes if/how a shortcut should be built for a package. +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct PackageShortcut { + pub name: String, + pub relative_path: String, + pub description: String, +} + /// Describes a overview of a individual package. #[derive(Debug, Deserialize, Serialize, Clone)] pub struct PackageDescription { @@ -26,6 +34,8 @@ pub struct PackageDescription { pub description: String, pub default: Option, pub source: PackageSource, + #[serde(default)] + pub shortcuts: Vec, } /// Describes the application itself. diff --git a/src/installer.rs b/src/installer.rs index e5eb8f8..3e4e470 100644 --- a/src/installer.rs +++ b/src/installer.rs @@ -26,6 +26,8 @@ use logging::LoggingErrors; use dirs::home_dir; +use std::fs::remove_file; + /// A message thrown during the installation of packages. #[derive(Serialize)] pub enum InstallMessage { @@ -61,7 +63,10 @@ pub struct InstallationStatus { pub struct LocalInstallation { pub name: String, pub version: Version, + /// Relative paths to generated files pub files: Vec, + /// Absolute paths to generated shortcut files + pub shortcuts: Vec, } impl InstallerFramework { @@ -148,7 +153,20 @@ impl InstallerFramework { if let Err(v) = messages.send(InstallMessage::Status(msg.to_string(), progress as _)) { error!("Failed to submit queue message: {:?}", v); } - }).map(|_x| ()) + }).map(|_x| ())?; + + // Delete the metadata file + let path = self + .install_path + .as_ref() + .log_expect("No install path specified"); + + remove_file(path.join("metadata.json")) + .map_err(|x| format!("Failed to delete metadata: {:?}", x))?; + + // Logging will have to be done later + + Ok(()) } /// Saves the applications database. diff --git a/src/main.rs b/src/main.rs index d003028..b67fedc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,6 +47,7 @@ mod config; mod http; mod installer; mod logging; +mod native; mod rest; mod sources; mod tasks; diff --git a/src/native/interop.cpp b/src/native/interop.cpp new file mode 100644 index 0000000..e97ea6d --- /dev/null +++ b/src/native/interop.cpp @@ -0,0 +1,89 @@ +/** + * Misc interop helpers. +**/ + +#include "windows.h" +#include "winnls.h" +#include "shobjidl.h" +#include "objbase.h" +#include "objidl.h" +#include "shlguid.h" + +extern "C" int saveShortcut( + const char *shortcutPath, + const char *description, + const char *path, + const char *args, + const char *workingDir) { + char* errStr = NULL; + HRESULT h; + IShellLink* shellLink = NULL; + IPersistFile* persistFile = NULL; + +#ifdef _WIN64 + wchar_t wName[MAX_PATH+1]; +#else + WORD wName[MAX_PATH+1]; +#endif + + int id; + + // Initialize the COM library + h = CoInitialize(NULL); + if (FAILED(h)) { + errStr = "Failed to initialize COM library"; + goto err; + } + + h = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_IShellLink, (PVOID*)&shellLink ); + if (FAILED(h)) { + errStr = "Failed to create IShellLink"; + goto err; + } + + h = shellLink->QueryInterface(IID_IPersistFile, (PVOID*)&persistFile); + if (FAILED(h)) { + errStr = "Failed to get IPersistFile"; + goto err; + } + + //Append the shortcut name to the folder + MultiByteToWideChar(CP_ACP,0,shortcutPath,-1,wName,MAX_PATH); + + // Load the file if it exists, to get the values for anything + // that we do not set. Ignore errors, such as if it does not exist. + h = persistFile->Load(wName, 0); + + // Set the fields for which the application has set a value + if (description!=NULL) + shellLink->SetDescription(description); + if (path!=NULL) + shellLink->SetPath(path); + if (args!=NULL) + shellLink->SetArguments(args); + if (workingDir!=NULL) + shellLink->SetWorkingDirectory(workingDir); + + //Save the shortcut to disk + h = persistFile->Save(wName, TRUE); + if (FAILED(h)) { + errStr = "Failed to save shortcut"; + goto err; + } + + persistFile->Release(); + shellLink->Release(); + CoUninitialize(); + return h; + +err: + if (persistFile != NULL) + persistFile->Release(); + if (shellLink != NULL) + shellLink->Release(); + CoUninitialize(); + + return h; +} + diff --git a/src/native/interop.h b/src/native/interop.h new file mode 100644 index 0000000..ec541b3 --- /dev/null +++ b/src/native/interop.h @@ -0,0 +1,6 @@ +int saveShortcut( + const char *shortcutPath, + const char *description, + const char *path, + const char *args, + const char *workingDir); diff --git a/src/native/mod.rs b/src/native/mod.rs new file mode 100644 index 0000000..64851e9 --- /dev/null +++ b/src/native/mod.rs @@ -0,0 +1,80 @@ +//! Natives/platform specific interactions. + +#[cfg(windows)] +mod natives { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + + use std::ffi::CString; + + use logging::LoggingErrors; + + use std::env; + + include!(concat!(env!("OUT_DIR"), "/interop.rs")); + + // Needed here for Windows interop + #[allow(unsafe_code)] + pub fn create_shortcut( + name: &str, + description: &str, + target: &str, + args: &str, + working_dir: &str, + ) -> Result { + let source_file = format!( + "{}\\Microsoft\\Windows\\Start Menu\\Programs\\{}.lnk", + env::var("APPDATA").log_expect("APPDATA is bad, apparently"), + name + ); + + info!("Generating shortcut @ {:?}", source_file); + + let native_target_dir = CString::new(source_file.clone()) + .log_expect("Error while converting to C-style string"); + let native_description = + CString::new(description).log_expect("Error while converting to C-style string"); + let native_target = + CString::new(target).log_expect("Error while converting to C-style string"); + let native_args = CString::new(args).log_expect("Error while converting to C-style string"); + let native_working_dir = + CString::new(working_dir).log_expect("Error while converting to C-style string"); + + let shortcutResult = unsafe { + saveShortcut( + native_target_dir.as_ptr(), + native_description.as_ptr(), + native_target.as_ptr(), + native_args.as_ptr(), + native_working_dir.as_ptr(), + ) + }; + + match shortcutResult { + 0 => Ok(source_file), + _ => Err(format!( + "Windows gave bad result while creating shortcut: {:?}", + shortcutResult + )), + } + } +} + +#[cfg(not(windows))] +mod natives { + pub fn create_shortcut( + name: &str, + description: &str, + target: &str, + args: &str, + working_dir: &str, + ) -> Result { + // TODO: no-op + warn!("create_shortcut is stubbed!"); + + Ok("".to_string()) + } +} + +pub use self::natives::*; diff --git a/src/rest.rs b/src/rest.rs index c389e6b..917a066 100644 --- a/src/rest.rs +++ b/src/rest.rs @@ -338,9 +338,17 @@ impl Service for WebService { thread::spawn(move || { let mut tx = tx; loop { - let response = receiver - .recv() - .log_expect("Failed to receive message from runner thread"); + let mut panic_after_finish = false; + + let response = match receiver + .recv() { + Ok(v) => v, + Err(v) => { + error!("Queue message failed: {:?}", v); + panic_after_finish = true; + InstallMessage::Error("Internal error".to_string()) + } + }; if let InstallMessage::EOF = response { break; @@ -353,6 +361,10 @@ impl Service for WebService { .send(Ok(response.into_bytes().into())) .wait() .log_expect("Failed to write JSON response payload to client"); + + if panic_after_finish { + panic!("Failed to read from queue (flushed error message successfully)"); + } } }); diff --git a/src/tasks/download_pkg.rs b/src/tasks/download_pkg.rs index fad92bd..9412b6a 100644 --- a/src/tasks/download_pkg.rs +++ b/src/tasks/download_pkg.rs @@ -3,6 +3,8 @@ use installer::InstallerFramework; use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskOrdering; use tasks::TaskParamType; use tasks::resolver::ResolvePackageTask; @@ -83,10 +85,13 @@ impl Task for DownloadPackageTask { Ok(TaskParamType::FileContents(version, file, data_storage)) } - fn dependencies(&self) -> Vec> { - vec![Box::new(ResolvePackageTask { - name: self.name.clone(), - })] + fn dependencies(&self) -> Vec { + vec![TaskDependency::build( + TaskOrdering::Pre, + Box::new(ResolvePackageTask { + name: self.name.clone(), + }), + )] } fn name(&self) -> String { diff --git a/src/tasks/install.rs b/src/tasks/install.rs index 8d6953c..4c6bc82 100644 --- a/src/tasks/install.rs +++ b/src/tasks/install.rs @@ -4,11 +4,12 @@ use installer::InstallerFramework; use tasks::install_dir::VerifyInstallDirTask; use tasks::install_pkg::InstallPackageTask; -use tasks::save_database::SaveDatabaseTask; use tasks::save_executable::SaveExecutableTask; use tasks::uninstall_pkg::UninstallPackageTask; use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskOrdering; use tasks::TaskParamType; pub struct InstallTask { @@ -28,28 +29,38 @@ impl Task for InstallTask { Ok(TaskParamType::None) } - fn dependencies(&self) -> Vec> { - let mut elements = Vec::>::new(); + fn dependencies(&self) -> Vec { + let mut elements = Vec::new(); - elements.push(Box::new(VerifyInstallDirTask { - clean_install: self.fresh_install, - })); + elements.push(TaskDependency::build( + TaskOrdering::Pre, + Box::new(VerifyInstallDirTask { + clean_install: self.fresh_install, + }), + )); for item in &self.items { - elements.push(Box::new(InstallPackageTask { name: item.clone() })); + elements.push(TaskDependency::build( + TaskOrdering::Pre, + Box::new(InstallPackageTask { name: item.clone() }), + )); } for item in &self.uninstall_items { - elements.push(Box::new(UninstallPackageTask { - name: item.clone(), - optional: false, - })); + elements.push(TaskDependency::build( + TaskOrdering::Pre, + Box::new(UninstallPackageTask { + name: item.clone(), + optional: false, + }), + )); } - elements.push(Box::new(SaveDatabaseTask {})); - if self.fresh_install { - elements.push(Box::new(SaveExecutableTask {})); + elements.push(TaskDependency::build( + TaskOrdering::Pre, + Box::new(SaveExecutableTask {}), + )); } elements diff --git a/src/tasks/install_dir.rs b/src/tasks/install_dir.rs index 0928aa4..503329d 100644 --- a/src/tasks/install_dir.rs +++ b/src/tasks/install_dir.rs @@ -3,6 +3,7 @@ use installer::InstallerFramework; use tasks::Task; +use tasks::TaskDependency; use tasks::TaskParamType; use std::fs::create_dir_all; @@ -46,7 +47,7 @@ impl Task for VerifyInstallDirTask { Ok(TaskParamType::None) } - fn dependencies(&self) -> Vec> { + fn dependencies(&self) -> Vec { vec![] } diff --git a/src/tasks/install_pkg.rs b/src/tasks/install_pkg.rs index 5beab6d..4242559 100644 --- a/src/tasks/install_pkg.rs +++ b/src/tasks/install_pkg.rs @@ -2,7 +2,13 @@ use installer::InstallerFramework; +use tasks::download_pkg::DownloadPackageTask; +use tasks::install_shortcuts::InstallShortcutsTask; +use tasks::save_database::SaveDatabaseTask; +use tasks::uninstall_pkg::UninstallPackageTask; use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskOrdering; use tasks::TaskParamType; use config::PackageDescription; @@ -11,9 +17,6 @@ use installer::LocalInstallation; use std::fs::create_dir_all; use std::io::copy; -use tasks::download_pkg::DownloadPackageTask; -use tasks::uninstall_pkg::UninstallPackageTask; - use logging::LoggingErrors; use archives; @@ -59,19 +62,23 @@ impl Task for InstallPackageTask { None => return Err(format!("Package {:?} could not be found.", self.name)), }; - // Check to see if no archive was available. - if let TaskParamType::Break = input - .pop() - .log_expect("Should have input from uninstaller!") - { - // No file to install, but all is good. - return Ok(TaskParamType::None); - } + // Grab data from the shortcut generator + let shortcuts = input.pop().log_expect("Should have input from resolver!"); + let shortcuts = match shortcuts { + TaskParamType::GeneratedShortcuts(files) => files, + // If the resolver returned early, we need to unwind + TaskParamType::Break => return Ok(TaskParamType::None), + _ => return Err("Unexpected shortcuts param type to install package".to_string()), + }; + // Ignore input from the uninstaller - no useful information passed + input.pop(); + + // Grab data from the resolver let data = input.pop().log_expect("Should have input from resolver!"); let (version, file, data) = match data { TaskParamType::FileContents(version, file, data) => (version, file, data), - _ => return Err("Unexpected param type to install package".to_string()), + _ => return Err("Unexpected file contents param type to install package".to_string()), }; let mut archive = archives::read_archive(&file.name, data.as_slice())?; @@ -159,21 +166,35 @@ impl Task for InstallPackageTask { context.database.push(LocalInstallation { name: package.name.to_owned(), version, + shortcuts, files: installed_files, }); Ok(TaskParamType::None) } - fn dependencies(&self) -> Vec> { + fn dependencies(&self) -> Vec { vec![ - Box::new(DownloadPackageTask { - name: self.name.clone(), - }), - Box::new(UninstallPackageTask { - name: self.name.clone(), - optional: true, - }), + TaskDependency::build( + TaskOrdering::Pre, + Box::new(DownloadPackageTask { + name: self.name.clone(), + }), + ), + TaskDependency::build( + TaskOrdering::Pre, + Box::new(UninstallPackageTask { + name: self.name.clone(), + optional: true, + }), + ), + TaskDependency::build( + TaskOrdering::Pre, + Box::new(InstallShortcutsTask { + name: self.name.clone(), + }), + ), + TaskDependency::build(TaskOrdering::Post, Box::new(SaveDatabaseTask {})), ] } diff --git a/src/tasks/install_shortcuts.rs b/src/tasks/install_shortcuts.rs new file mode 100644 index 0000000..dae4967 --- /dev/null +++ b/src/tasks/install_shortcuts.rs @@ -0,0 +1,98 @@ +//! Generates shortcuts for a specified file. + +use installer::InstallerFramework; + +use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskParamType; + +use config::PackageDescription; + +use logging::LoggingErrors; + +use native::create_shortcut; + +pub struct InstallShortcutsTask { + pub name: String, +} + +impl Task for InstallShortcutsTask { + fn execute( + &mut self, + _: Vec, + context: &mut InstallerFramework, + messenger: &Fn(&str, f64), + ) -> Result { + messenger( + &format!("Generating shortcuts for package {:?}...", self.name), + 0.0, + ); + + let path = context + .install_path + .as_ref() + .log_expect("No install path specified"); + + let starting_dir = path + .to_str() + .log_expect("Unable to build shortcut metadata (startingdir)"); + + let mut installed_files = Vec::new(); + + let mut metadata: Option = None; + for description in &context + .config + .as_ref() + .log_expect("Should have packages by now") + .packages + { + if self.name == description.name { + metadata = Some(description.clone()); + break; + } + } + + let package = match metadata { + Some(v) => v, + None => return Err(format!("Package {:?} could not be found.", self.name)), + }; + + // Generate installer path + let platform_extension = if cfg!(windows) { + "maintenancetool.exe" + } else { + "maintenancetool" + }; + + for shortcut in package.shortcuts { + let tool_path = path.join(platform_extension); + let tool_path = tool_path + .to_str() + .log_expect("Unable to build shortcut metadata (tool)"); + + let exe_path = path.join(shortcut.relative_path); + let exe_path = exe_path + .to_str() + .log_expect("Unable to build shortcut metadata (exe)"); + + installed_files.push(create_shortcut( + &shortcut.name, + &shortcut.description, + tool_path, + // TODO: Send by list + &format!("--launcher \"{}\"", exe_path), + &starting_dir, + )?); + } + + Ok(TaskParamType::GeneratedShortcuts(installed_files)) + } + + fn dependencies(&self) -> Vec { + vec![] + } + + fn name(&self) -> String { + format!("InstallShortcutsTask (for {:?})", self.name) + } +} diff --git a/src/tasks/mod.rs b/src/tasks/mod.rs index 2022f92..1aea805 100644 --- a/src/tasks/mod.rs +++ b/src/tasks/mod.rs @@ -13,11 +13,13 @@ pub mod download_pkg; pub mod install; pub mod install_dir; pub mod install_pkg; +pub mod install_shortcuts; pub mod resolver; pub mod save_database; pub mod save_executable; pub mod uninstall; pub mod uninstall_pkg; +pub mod uninstall_shortcuts; /// An abstraction over the various parameters that can be passed around. pub enum TaskParamType { @@ -26,10 +28,34 @@ pub enum TaskParamType { File(Version, File), /// Downloaded contents of a file FileContents(Version, File, Vec), + /// List of shortcuts that have been generated + GeneratedShortcuts(Vec), /// Tells the runtime to break parsing other dependencies Break, } +/// Specifies the relative ordering of a dependency with it's parent. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub enum TaskOrdering { + /// This task should occur before the main process + Pre, + /// This task should occur after the main process. These have their results discarded. + Post, +} + +/// A dependency of a task with various properties. +pub struct TaskDependency { + ordering: TaskOrdering, + task: Box, +} + +impl TaskDependency { + /// Builds a new dependency from the specified task. + pub fn build(ordering: TaskOrdering, task: Box) -> TaskDependency { + TaskDependency { ordering, task } + } +} + /// A Task is a small, async task conforming to a fixed set of inputs/outputs. pub trait Task { /// Executes this individual task, evaluating to the given Output result. @@ -44,7 +70,7 @@ pub trait Task { /// Returns a vector containing all dependencies that need to be executed /// before this task can function. - fn dependencies(&self) -> Vec>; + fn dependencies(&self) -> Vec; /// Returns a short name used for formatting the dependency tree. fn name(&self) -> String; @@ -53,7 +79,7 @@ pub trait Task { /// The dependency tree allows for smart iteration on a Task struct. pub struct DependencyTree { task: Box, - dependencies: Vec, + dependencies: Vec<(TaskOrdering, DependencyTree)>, } impl DependencyTree { @@ -64,8 +90,9 @@ impl DependencyTree { buf += "\n"; for i in 0..self.dependencies.len() { - let dependencies = self.dependencies[i].render(); - let dependencies = dependencies.trim(); + let (order, dependency) = &(self.dependencies[i]); + let dependencies = dependency.render(); + let dependencies = format!("{:?} {}", order, dependencies.trim()); if i + 1 == self.dependencies.len() { buf += "└── "; @@ -92,7 +119,11 @@ impl DependencyTree { let mut count = 0; - for i in &mut self.dependencies { + for (ordering, i) in &mut self.dependencies { + if ordering != &TaskOrdering::Pre { + continue; + } + let result = i.execute(context, &|msg: &str, progress: f64| { messenger( msg, @@ -114,13 +145,46 @@ impl DependencyTree { } } - self.task + let task_result = self + .task .execute(inputs, context, &|msg: &str, progress: f64| { messenger( msg, progress / total_tasks + (1.0 / total_tasks) * f64::from(count), ) - }) + })?; + + if let TaskParamType::Break = task_result { + // We are done here + return Ok(TaskParamType::Break); + } + + for (ordering, i) in &mut self.dependencies { + if ordering != &TaskOrdering::Post { + continue; + } + + let result = i.execute(context, &|msg: &str, progress: f64| { + messenger( + msg, + progress / total_tasks + (1.0 / total_tasks) * f64::from(count), + ) + })?; + + // Check to see if we skip matching other dependencies + let do_break = match &result { + TaskParamType::Break => true, + _ => false, + }; + + count += 1; + + if do_break { + break; + } + } + + Ok(task_result) } /// Builds a new pipeline from the specified task, iterating on dependencies. @@ -128,7 +192,7 @@ impl DependencyTree { let dependencies = task .dependencies() .into_iter() - .map(|x| DependencyTree::build(x)) + .map(|x| (x.ordering, DependencyTree::build(x.task))) .collect(); DependencyTree { task, dependencies } diff --git a/src/tasks/resolver.rs b/src/tasks/resolver.rs index 5e5df5c..1034bb7 100644 --- a/src/tasks/resolver.rs +++ b/src/tasks/resolver.rs @@ -5,6 +5,7 @@ use std::env::consts::OS; use installer::InstallerFramework; use tasks::Task; +use tasks::TaskDependency; use tasks::TaskParamType; use config::PackageDescription; @@ -89,7 +90,7 @@ impl Task for ResolvePackageTask { Ok(TaskParamType::File(latest_version, latest_file)) } - fn dependencies(&self) -> Vec> { + fn dependencies(&self) -> Vec { vec![] } diff --git a/src/tasks/save_database.rs b/src/tasks/save_database.rs index 33f2ee5..b030345 100644 --- a/src/tasks/save_database.rs +++ b/src/tasks/save_database.rs @@ -3,6 +3,7 @@ use installer::InstallerFramework; use tasks::Task; +use tasks::TaskDependency; use tasks::TaskParamType; pub struct SaveDatabaseTask {} @@ -22,7 +23,7 @@ impl Task for SaveDatabaseTask { Ok(TaskParamType::None) } - fn dependencies(&self) -> Vec> { + fn dependencies(&self) -> Vec { vec![] } diff --git a/src/tasks/save_executable.rs b/src/tasks/save_executable.rs index 3d25fda..ef8d593 100644 --- a/src/tasks/save_executable.rs +++ b/src/tasks/save_executable.rs @@ -3,6 +3,7 @@ use installer::InstallerFramework; use tasks::Task; +use tasks::TaskDependency; use tasks::TaskParamType; use std::fs::File; @@ -71,7 +72,7 @@ impl Task for SaveExecutableTask { Ok(TaskParamType::None) } - fn dependencies(&self) -> Vec> { + fn dependencies(&self) -> Vec { vec![] } diff --git a/src/tasks/uninstall.rs b/src/tasks/uninstall.rs index 628b113..006068f 100644 --- a/src/tasks/uninstall.rs +++ b/src/tasks/uninstall.rs @@ -5,8 +5,9 @@ use installer::InstallerFramework; use tasks::Task; use tasks::TaskParamType; -use tasks::save_database::SaveDatabaseTask; use tasks::uninstall_pkg::UninstallPackageTask; +use tasks::TaskDependency; +use tasks::TaskOrdering; pub struct UninstallTask { pub items: Vec, @@ -23,18 +24,19 @@ impl Task for UninstallTask { Ok(TaskParamType::None) } - fn dependencies(&self) -> Vec> { - let mut elements = Vec::>::new(); + fn dependencies(&self) -> Vec { + let mut elements = Vec::new(); for item in &self.items { - elements.push(Box::new(UninstallPackageTask { - name: item.clone(), - optional: false, - })); + elements.push(TaskDependency::build( + TaskOrdering::Pre, + Box::new(UninstallPackageTask { + name: item.clone(), + optional: false, + }), + )); } - elements.push(Box::new(SaveDatabaseTask {})); - elements } diff --git a/src/tasks/uninstall_pkg.rs b/src/tasks/uninstall_pkg.rs index 23b365c..d33018a 100644 --- a/src/tasks/uninstall_pkg.rs +++ b/src/tasks/uninstall_pkg.rs @@ -2,7 +2,10 @@ use installer::InstallerFramework; +use tasks::save_database::SaveDatabaseTask; use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskOrdering; use tasks::TaskParamType; use installer::LocalInstallation; @@ -11,6 +14,7 @@ use std::fs::remove_dir; use std::fs::remove_file; use logging::LoggingErrors; +use tasks::uninstall_shortcuts::UninstallShortcutsTask; pub struct UninstallPackageTask { pub name: String, @@ -24,7 +28,7 @@ impl Task for UninstallPackageTask { context: &mut InstallerFramework, messenger: &Fn(&str, f64), ) -> Result { - assert_eq!(input.len(), 0); + assert_eq!(input.len(), 1); let path = context .install_path @@ -83,8 +87,17 @@ impl Task for UninstallPackageTask { Ok(TaskParamType::None) } - fn dependencies(&self) -> Vec> { - vec![] + fn dependencies(&self) -> Vec { + vec![ + TaskDependency::build( + TaskOrdering::Pre, + Box::new(UninstallShortcutsTask { + name: self.name.clone(), + optional: self.optional, + }), + ), + TaskDependency::build(TaskOrdering::Post, Box::new(SaveDatabaseTask {})), + ] } fn name(&self) -> String { diff --git a/src/tasks/uninstall_shortcuts.rs b/src/tasks/uninstall_shortcuts.rs new file mode 100644 index 0000000..8fb4c90 --- /dev/null +++ b/src/tasks/uninstall_shortcuts.rs @@ -0,0 +1,100 @@ +//! Uninstalls a specific package. + +use installer::InstallerFramework; + +use tasks::Task; +use tasks::TaskDependency; +use tasks::TaskParamType; + +use installer::LocalInstallation; + +use std::fs::remove_dir; +use std::fs::remove_file; + +use logging::LoggingErrors; + +pub struct UninstallShortcutsTask { + pub name: String, + pub optional: bool, +} + +impl Task for UninstallShortcutsTask { + fn execute( + &mut self, + input: Vec, + context: &mut InstallerFramework, + messenger: &Fn(&str, f64), + ) -> Result { + assert_eq!(input.len(), 0); + + let path = context + .install_path + .as_ref() + .log_expect("No install path specified"); + + let mut metadata: Option = None; + for i in 0..context.database.len() { + if self.name == context.database[i].name { + metadata = Some(context.database[i].clone()); + break; + } + } + + let mut package = match metadata { + Some(v) => v, + None => { + if self.optional { + return Ok(TaskParamType::None); + } + + return Err(format!( + "Package {:?} could not be found for uninstall.", + self.name + )); + } + }; + + messenger( + &format!("Uninstalling shortcuts for package {:?}...", self.name), + 0.0, + ); + + // Reverse, as to delete directories last + package.files.reverse(); + + let max = package.files.len(); + for (i, file) in package.shortcuts.iter().enumerate() { + let name = file.clone(); + let file = path.join(file); + info!("Deleting {:?}", file); + + messenger( + &format!("Deleting {} ({} of {})", name, i + 1, max), + (i as f64) / (max as f64), + ); + + let result = if file.is_dir() { + remove_dir(file) + } else { + remove_file(file) + }; + + if let Err(v) = result { + error!("Failed to delete file: {:?}", v); + } + } + + Ok(TaskParamType::None) + } + + fn dependencies(&self) -> Vec { + vec![] + } + + fn name(&self) -> String { + format!( + "UninstallShortcutsTask (for {:?}, optional = {})", + self.name, self.optional + ) + } +}