Add python setup script

This commit is contained in:
doyle 2023-01-29 20:46:57 +11:00
parent d266705655
commit 2c1dd67b5e
6 changed files with 3197 additions and 0 deletions

View File

@ -0,0 +1,459 @@
---
IndentWidth: 4
TabWidth: 4
---
Language: Cpp
# Align parameters on the open bracket, e.g.:
# someLongFunction(argument1,
# argument2);
AlignAfterOpenBracket: Align
# Align array column and left justify the columns e.g.:
# struct test demo[] =
# {
# {56, 23, "hello"},
# {-1, 93463, "world"},
# {7, 5, "!!" }
# };
AlignArrayOfStructures: Left
# Align assignments on consecutive lines. This will result in formattings like:
#
# int a = 1;
# int somelongname = 2;
# double c = 3;
#
# int d = 3;
# /* A comment. */
# double e = 4;
AlignConsecutiveAssignments: Consecutive
AlignConsecutiveBitFields: Consecutive
AlignConsecutiveDeclarations: Consecutive
AlignConsecutiveMacros: Consecutive
# Align escaped newlines as far left as possible.
# #define A \
# int aaaa; \
# int b; \
# int dddddddddd;
AlignEscapedNewlines: Left
# Horizontally align operands of binary and ternary expressions.
# Specifically, this aligns operands of a single expression that needs to be
# split over multiple lines, e.g.:
#
# int aaa = bbbbbbbbbbbbbbb +
# ccccccccccccccc;
AlignOperands: Align
# true: false:
# int a; // My comment a vs. int a; // My comment a
# int b = 2; // comment b int b = 2; // comment about b
AlignTrailingComments: true
# If the function declaration doesnt fit on a line, allow putting all
# parameters of a function declaration onto the next line even if
# BinPackParameters is false.
#
# true:
# void myFunction(
# int a, int b, int c, int d, int e);
#
# false:
# void myFunction(int a,
# int b,
# int c,
# int d,
# int e);
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never # "while (true) { continue; }" can be put on a single line.
# If true, short case labels will be contracted to a single line.
#
# true: false:
# switch (a) { vs. switch (a) {
# case 1: x = 1; break; case 1:
# case 2: return; x = 1;
# } break;
# case 2:
# return;
# }
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: true # enum { A, B } myEnum;
# Only merge functions defined inside a class. Implies “empty”.
#
# class Foo {
# void f() { foo(); }
# };
# void f() {
# foo();
# }
# void f() {}
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
# Only merge empty lambdas.
#
# auto lambda = [](int a) {}
# auto lambda2 = [](int a) {
# return a;
# };
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
# true: false:
# aaaa = vs. aaaa = "bbbb"
# "bbbb" "cccc";
# "cccc";
AlwaysBreakBeforeMultilineStrings: true
# Force break after template declaration only when the following declaration
# spans multiple lines.
#
# template <typename T> T foo() {
# }
# template <typename T>
# T foo(int aaaaaaaaaaaaaaaaaaaaa,
# int bbbbbbbbbbbbbbbbbbbbb) {
# }
AlwaysBreakTemplateDeclarations: MultiLine
# If false, a function calls arguments will either be all on the same line or
# will have one line each.
#
# true:
# void f() {
# f(aaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaa,
# aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
# }
#
# false:
# void f() {
# f(aaaaaaaaaaaaaaaaaaaa,
# aaaaaaaaaaaaaaaaaaaa,
# aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);
# }
BinPackArguments: false
BinPackParameters: false # As BinPackArguments but for function definition parameters
# Add space after the : only (space may be added before if needed for
# AlignConsecutiveBitFields).
#
# unsigned bf: 2;
BitFieldColonSpacing: After
# LooooooooooongType loooooooooooooooooooooongVariable =
# someLooooooooooooooooongFunction();
#
# bool value = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
# aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==
# aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
# aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >
# ccccccccccccccccccccccccccccccccccccccccc;
BreakBeforeBinaryOperators: None
# Always attach braces to surrounding context, but break before braces on
# function, namespace and class definitions.
BreakBeforeBraces: Linux
# true:
# template<typename T>
# concept ...
#
# false:
# template<typename T> concept ...
BreakBeforeConceptDeclarations: false
# true:
# veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongDescription
# ? firstValue
# : SecondValueVeryVeryVeryVeryLong;
#
# false:
# veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongDescription ?
# firstValue :
# SecondValueVeryVeryVeryVeryLong;
BreakBeforeTernaryOperators: true
# Break constructor initializers before the colon and commas, and align the
# commas with the colon.
#
# Constructor()
# : initializer1()
# , initializer2()
BreakConstructorInitializers: BeforeComma
# Break inheritance list only after the commas.
#
# class Foo : Base1,
# Base2
# {};
BreakInheritanceList: AfterComma
# true:
# const char* x = "veryVeryVeryVeryVeryVe"
# "ryVeryVeryVeryVeryVery"
# "VeryLongString";
BreakStringLiterals: true
ColumnLimit: 100
# false:
# namespace Foo {
# namespace Bar {
# }
# }
CompactNamespaces: false
# true: false:
# vector<int> x{1, 2, 3, 4}; vs. vector<int> x{ 1, 2, 3, 4 };
# vector<T> x{{}, {}, {}, {}}; vector<T> x{ {}, {}, {}, {} };
# f(MyMap[{composite, key}]); f(MyMap[{ composite, key }]);
# new int[3]{1, 2, 3}; new int[3]{ 1, 2, 3 };
Cpp11BracedListStyle: true
# Analyze the formatted file for the most used line ending (\r\n or \n). UseCRLF
# is only used as a fallback if none can be derived.
DeriveLineEnding: true
DerivePointerAlignment: true # As per DeriveLineEnding except for pointers and references
# Add empty line only when access modifier starts a new logical block. Logical
# block is a group of one or more member fields or functions.
#
# struct foo {
# private:
# int i;
#
# protected:
# int j;
# /* comment */
# public:
# foo() {}
#
# private:
# protected:
# };
EmptyLineBeforeAccessModifier: LogicalBlock
# true: false:
# namespace a { vs. namespace a {
# foo(); foo();
# bar(); bar();
# } // namespace a }
FixNamespaceComments: true
# false: true:
# class C { vs. class C {
# class D { class D {
# void bar(); void bar();
# protected: protected:
# D(); D();
# }; };
# public: public:
# C(); C();
# }; };
# void foo() { void foo() {
# return 1; return 1;
# } }
IndentAccessModifiers: false
# false: true:
# switch (fool) { vs. switch (fool) {
# case 1: { case 1:
# bar(); {
# } break; bar();
# default: { }
# plop(); break;
# } default:
# } {
# plop();
# }
# }
IndentCaseBlocks: false
# false: true:
# switch (fool) { vs. switch (fool) {
# case 1: case 1:
# bar(); bar();
# break; break;
# default: default:
# plop(); plop();
# } }
IndentCaseLabels: true
# extern "C" {
# void foo();
# }
IndentExternBlock: NoIndent
# Indents directives before the hash.
#
# #if FOO
# #if BAR
# #include <foo>
# #endif
# #endif
IndentPPDirectives: BeforeHash
# true: false:
# if (foo) { vs. if (foo) {
# bar();
# bar(); }
# }
KeepEmptyLinesAtTheStartOfBlocks: false
# The maximum number of consecutive empty lines to keep.
#
# MaxEmptyLinesToKeep: 1 vs. MaxEmptyLinesToKeep: 0
# int f() { int f() {
# int = 1; int i = 1;
# i = foo();
# i = foo(); return i;
# }
# return i;
# }
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
# Put all constructor initializers on the current line if they fit. Otherwise,
# put each one on its own line.
#
# Constructor() : a(), b()
#
# Constructor()
# : aaaaaaaaaaaaaaaaaaaa(),
# bbbbbbbbbbbbbbbbbbbb(),
# ddddddddddddd()
PackConstructorInitializers: CurrentLine
PointerAlignment: Right
# false:
# // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information
# /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */
#
# true:
# // veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
# // information
# /* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of
# * information */
ReflowComments: true
# false: true:
#
# if (isa<FunctionDecl>(D)) { vs. if (isa<FunctionDecl>(D))
# handleFunctionDecl(D); handleFunctionDecl(D);
# } else if (isa<VarDecl>(D)) { else if (isa<VarDecl>(D))
# handleVarDecl(D); handleVarDecl(D);
# }
#
# if (isa<VarDecl>(D)) { vs. if (isa<VarDecl>(D)) {
# for (auto *A : D.attrs()) { for (auto *A : D.attrs())
# if (shouldProcessAttr(A)) { if (shouldProcessAttr(A))
# handleAttr(A); handleAttr(A);
# } }
# }
# }
#
# if (isa<FunctionDecl>(D)) { vs. if (isa<FunctionDecl>(D))
# for (auto *A : D.attrs()) { for (auto *A : D.attrs())
# handleAttr(A); handleAttr(A);
# }
# }
#
# if (auto *D = (T)(D)) { vs. if (auto *D = (T)(D)) {
# if (shouldProcess(D)) { if (shouldProcess(D))
# handleVarDecl(D); handleVarDecl(D);
# } else { else
# markAsIgnored(D); markAsIgnored(D);
# } }
# }
#
# if (a) { vs. if (a)
# b(); b();
# } else { else if (c)
# if (c) { d();
# d(); else
# } else { e();
# e();
# }
# }
RemoveBracesLLVM: true
# Never v.s. Always
# #include <cstring> #include <cstring>
# struct Foo {
# int a, b, c; struct Foo {
# }; int a, b, c;
# namespace Ns { };
# class Bar {
# public: namespace Ns {
# struct Foobar { class Bar {
# int a; public:
# int b; struct Foobar {
# }; int a;
# private: int b;
# int t; };
# int method1() {
# // ... private:
# } int t;
# enum List {
# ITEM1, int method1() {
# ITEM2 // ...
# }; }
# template<typename T>
# int method2(T x) { enum List {
# // ... ITEM1,
# } ITEM2
# int i, j, k; };
# int method3(int par) {
# // ... template<typename T>
# } int method2(T x) {
# }; // ...
# class C {}; }
# }
# int i, j, k;
#
# int method3(int par) {
# // ...
# }
# };
#
# class C {};
# }
SeparateDefinitionBlocks: Always
# true: false:
# int a = 5; vs. int a= 5;
# a += 42; a+= 42;
SpaceBeforeAssignmentOperators: true
# Put a space before opening parentheses only after control statement keywords
# (for/if/while...).
#
# void f() {
# if (true) {
# f();
# }
# }
SpaceBeforeParens: ControlStatements
SpacesBeforeTrailingComments: 1
# static_cast<int>(arg);
# std::function<void(int)> fct;
SpacesInAngles: Never
Standard: Auto
# Macros which are ignored in front of a statement, as if they were an
# attribute. So that they are not parsed as identifier, for example for Qts
# emit.
# unsigned char data = 'x';
# emit signal(data); // This is parsed as variable declaration.
#
# vs.
#
# unsigned char data = 'x';
# emit signal(data); // Now it's fine again.
StatementAttributeLikeMacros: [emit]
---

377
Internal/os_nvim_init.vim Normal file
View File

@ -0,0 +1,377 @@
" Plugins
" ==============================================================================
call plug#begin(stdpath('config') . '/plugged')
" nerdtree provides a file tree explorer
" vim-dispatch allows running async jobs in vim (i.e. builds in the background)
Plug 'https://github.com/scrooloose/nerdtree', { 'on': 'NERDTreeToggle' }
Plug 'https://github.com/tpope/vim-dispatch'
Plug 'https://github.com/tpope/vim-fugitive'
Plug 'https://github.com/tpope/vim-abolish'
" TODO: 2022-06-19 Treesitter is too slow on large C++ files
" Plug 'https://github.com/nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
Plug 'https://github.com/bfrg/vim-cpp-modern'
" FZF
Plug 'junegunn/fzf'
Plug 'junegunn/fzf.vim'
" FZF for LSP
Plug 'gfanto/fzf-lsp.nvim'
Plug 'nvim-lua/plenary.nvim'
" odin for syntax highlighting
Plug 'https://github.com/Tetralux/odin.vim'
Plug 'https://github.com/sainnhe/gruvbox-material'
" Lua cache to speed up load times
Plug 'https://github.com/lewis6991/impatient.nvim'
" lsp-zero begin
" LSP Support
Plug 'neovim/nvim-lspconfig'
Plug 'williamboman/mason.nvim'
Plug 'williamboman/mason-lspconfig.nvim'
" Autocompletion
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-buffer'
Plug 'hrsh7th/cmp-path'
Plug 'saadparwaiz1/cmp_luasnip'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/cmp-nvim-lua'
" Snippets
Plug 'L3MON4D3/LuaSnip'
" Snippet collection (Optional)
Plug 'rafamadriz/friendly-snippets'
Plug 'VonHeikemen/lsp-zero.nvim'
" lsp-zero end
call plug#end()
" Lua Setup
" ==============================================================================
lua <<EOF
require('impatient')
-- LSP Setup
-- ===========================================================================
local lsp = require('lsp-zero')
lsp.preset('recommended')
lsp.setup()
-- Treesitter
-- ===========================================================================
-- TODO: 2022-06-19 Treesitter is too slow on large C++ files
-- require('nvim-treesitter.configs').setup {
-- ensure_installed = { "c", "cpp" }, -- A list of parser names, or "all"
-- sync_install = false, -- Install parsers synchronously (only applied to `ensure_installed`)
-- ignore_install = { }, -- List of parsers to ignore installing (for "all")
-- highlight = {
-- enable = false, -- `false` will disable the whole extension
-- -- NOTE: these are the names of the parsers and not the filetype. (for example if you want to
-- -- disable highlighting for the `tex` filetype, you need to include `latex` in this list as this is
-- -- the name of the parser)
-- -- list of language that will be disabled
-- disable = { },
-- -- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- -- Using this option may slow down your editor, and you may see some duplicate highlights.
-- -- Instead of true it can also be a list of languages
-- additional_vim_regex_highlighting = false,
-- },
-- }
-- Vim Options
-- ===========================================================================
vim.opt.autowrite=true -- Automatically save before cmds like :next and :prev
vim.opt.colorcolumn={80, 100} -- Set a 80 and 100 char column ruler
vim.opt.completeopt={'menu', 'menuone', 'noselect'}
vim.opt.cpoptions:append('$') -- $ as end marker for the change operator
vim.opt.cursorline=true -- Highlight current line
vim.opt.expandtab=true -- Replace tabs with spaces
vim.opt.guifont={'JetBrains_Mono:h9',
'Consolas:h9',
'InputMonoCondensed:h9'}
vim.opt.hlsearch=false -- Highlight just the first match on search
vim.opt.ignorecase=true -- Search is not case sensitive
vim.opt.linebreak=true -- On wrapped lines, break on the wrapping word intelligently
vim.opt.list=true -- Show the 'listchar' characters on trailing spaces, tabs e.t.c
vim.opt.listchars:append('tab:>-,trail:■,extends:»,precedes:«')
vim.opt.number=true -- Show line numbers
vim.opt.relativenumber=true -- Show relative line numbers
vim.opt.shiftwidth=4 -- Number of spaces for each autoindent step
vim.opt.splitright=true -- Open new splits to the right of the current one
vim.opt.swapfile=false -- Disable swapfile (stores the things changed in a file)
vim.opt.textwidth=80 -- On format, format to 80 char long lines
vim.opt.visualbell=true -- Flash the screen on error
vim.opt.wrap=false -- Don't wrap lines of text automatically
vim.opt.signcolumn = 'no'
vim.diagnostic.config({
-- Turn off the diagnostics signs on the line number. In LSP mode, editing
-- a C++ buffer constantly toggles the sign column on and off as you change
-- modes which is very visually distracting.
signs = false,
})
-- Check if there were args (i.e. opened file), non-empty buffer, or started in insert mode
if vim.fn.argc() == 0 or vim.fn.line2byte("$") ~= -1 and not opt.insertmode then
local ascii = {
"",
" Useful Bindings (Normal Mode)",
" --------------------------------------------------",
" <Ctrl+n> to open the file tree explorer",
" <Ctrl+i> clang format selected lines",
" <Ctrl+j> jump to next compilation error",
" <Ctrl+k> jump to prev compilation error",
" <cd> change working directory to current file",
" <\\s> split buffer vertically",
"",
" Abolish (Text Substitution in Normal Mode)",
" --------------------------------------------------",
" %S/facilit{y,ies}/building{,s}/g Convert facility->building, facilities->buildings",
" %S/action/sleep/g Convert action to sleep, (preserve case sensitivity ACTION->SLEEP, action->sleep) ",
"",
" FZF (Normal Mode)",
" --------------------------------------------------",
" <\\h> vim command history",
" <\\f> find files",
" <\\g> search for text (via ripgrep)",
" <\\tt> search for tag (global)",
" <\\tb> search for tag (buffer)",
" <\\cc> search for commit (global)",
" <\\cb> search for commit (buffer)",
" <\\b> search for buffer",
"",
" Autocompletion (nvim-cmp in Normal Mode)",
" --------------------------------------------------",
" <Enter> Confirms selection.",
" <Ctrl-y> Confirms selection.",
" <Up> Navigate to previous item on the list.",
" <Down> Navigate to the next item on the list.",
" <Ctrl-p> Navigate to previous item on the list.",
" <Ctrl-n> Navigate to the next item on the list.",
" <Ctrl-u> Scroll up in the item's documentation.",
" <Ctrl-f> Scroll down in the item's documentation.",
" <Ctrl-e> Toggles the completion.",
" <Ctrl-d> Go to the next placeholder in the snippet.",
" <Ctrl-b> Go to the previous placeholder in the snippet.",
" <Tab> Enables completion when the cursor is inside a word. If the completion menu is visible it will navigate to the next item in the list.",
" <Shift-Tab> When the completion menu is visible navigate to the previous item in the list.",
"",
" LSP Bindings (Normal Mode)",
" --------------------------------------------------",
" <Shift-K> Displays hover information about the symbol under the cursor in a floating window. See help vim.lsp.buf.hover().",
" gd Jumps to the definition of the symbol under the cursor. See help vim.lsp.buf.definition().",
" gD Jumps to the declaration of the symbol under the cursor. Some servers don't implement this feature. See help vim.lsp.buf.declaration().",
" gi Lists all the implementations for the symbol under the cursor in the quickfix window. See help vim.lsp.buf.implementation().",
" go Jumps to the definition of the type of the symbol under the cursor. See help vim.lsp.buf.type_definition().",
" gr Lists all the references to the symbol under the cursor in the quickfix window. See help vim.lsp.buf.references().",
" <Ctrl-k> Displays signature information about the symbol under the cursor in a floating window. See help vim.lsp.buf.signature_help(). If a mapping already exists for this key this function is not bound.",
" <F2> Renames all references to the symbol under the cursor. See help vim.lsp.buf.rename().",
" <F4> Selects a code action available at the current cursor position. See help vim.lsp.buf.code_action().",
" gl Show diagnostics in a floating window. See :help vim.diagnostic.open_float().",
" [d Move to the previous diagnostic in the current buffer. See :help vim.diagnostic.goto_prev().",
" ]d Move to the next diagnostic. See :help vim.diagnostic.goto_next()."
}
local height = vim.api.nvim_get_option("lines")
local width = vim.api.nvim_get_option("columns")
local ascii_rows = #ascii
local ascii_cols = #ascii[1]
local win = vim.api.nvim_get_current_win()
local buf = vim.api.nvim_create_buf(true, true)
local function reset_start_screen()
vim.cmd("enew")
local buf = vim.api.nvim_get_current_buf()
local win = vim.api.nvim_get_current_win()
vim.api.nvim_buf_set_option(buf, "modifiable", true)
vim.api.nvim_buf_set_option(buf, "buflisted", true)
vim.api.nvim_buf_set_option(buf, "buflisted", true)
end
vim.api.nvim_buf_set_lines(buf, 0, -1, false, ascii)
vim.api.nvim_buf_set_option(buf, "modified", false)
vim.api.nvim_buf_set_option(buf, "buflisted", false)
vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe")
vim.api.nvim_buf_set_option(buf, "buftype", "nofile")
vim.api.nvim_buf_set_option(buf, "swapfile", false)
vim.api.nvim_set_current_buf(buf)
vim.api.nvim_create_autocmd("InsertEnter,WinEnter", {
pattern = "<buffer>",
callback = reset_start_screen,
})
end
EOF
" Theme
" ==============================================================================
let g:gruvbox_material_background='hard'
let g:gruvbox_material_foreground='mix'
let g:gruvbox_material_disable_italic_comment=1
let g:gruvbox_material_enable_italic=0
let g:gruvbox_material_enable_bold=0
let g:gruvbox_material_diagnostic_virtual_text='colored'
let g:gruvbox_material_better_performance=1
colorscheme gruvbox-material
" Vim-cpp-modern customisation
" Disable function highlighting (affects both C and C++ files)
let g:cpp_function_highlight = 1
" Enable highlighting of C++11 attributes
let g:cpp_attributes_highlight = 1
" Highlight struct/class member variables (affects both C and C++ files)
let g:cpp_member_highlight = 0
" Put all standard C and C++ keywords under Vim's highlight group 'Statement'
" (affects both C and C++ files)
let g:cpp_simple_highlight = 1
" Options
" ==============================================================================
" Show EOL type and last modified timestamp, right after the filename
set statusline=%<%F%h%m%r\ [%{&ff}]\ (%{strftime(\"%H:%M\ %d/%m/%Y\",getftime(expand(\"%:p\")))})%=%l,%c%V\ %P
" Resize splits when the window is resized
au VimResized * :wincmd =
" File patterns to ignore in command line auto complete
set wildignore+=*.class,*.o
set wildignore+=*\\tmp\\*,*.swp,*.zip,*.exe,*.obj,*.vcxproj,*.pdb,*.idb
" Setup undo file
set undofile
let &undodir=stdpath('config') . '/undo'
" Setup backup directory
let &backupdir=stdpath('config') . '/backup'
" Enable mouse support
if has('mouse')
set mouse=a
endif
" Functions
" ==============================================================================
" Increase font size using (Ctrl+Up Arrow) or (Ctrl+Down Arrow) if we are using
" gvim Otherwise font size is determined in terminal
nnoremap <C-Up> :silent! let &guifont = substitute(
\ &guifont,
\ ':h\zs\d\+',
\ '\=eval(submatch(0)+1)',
\ 'g')<CR>
nnoremap <C-Down> :silent! let &guifont = substitute(
\ &guifont,
\ ':h\zs\d\+',
\ '\=eval(submatch(0)-1)',
\ 'g')<CR>
" Formatting options (see :h fo-table)
augroup persistent_settings
au!
au bufenter * :set formatoptions=q1j
augroup end
" FZF
" ==============================================================================
" Empty value to disable preview window altogether
let g:fzf_preview_window = []
" Prefix all commands with Fzf for discoverability
let g:fzf_command_prefix = 'Fzf'
" - down / up / left / right
let g:fzf_layout = { 'down': '40%' }
" Add "FzfCustomRG" command which reinitializes
function! RipgrepFzf(query, fullscreen)
let command_fmt = 'rg --column --line-number --no-heading --color=always --smart-case -- %s || true'
let initial_command = printf(command_fmt, shellescape(a:query))
let reload_command = printf(command_fmt, '{q}')
let spec = {'options': ['--phony', '--query', a:query, '--bind', 'change:reload:'.reload_command]}
call fzf#vim#grep(initial_command, 1, fzf#vim#with_preview(spec), a:fullscreen)
endfunction
command! -nargs=* -bang FzfCustomRG call RipgrepFzf(<q-args>, <bang>0)
" Augment the "FzfCustomFiles" command
command! -bang -nargs=? -complete=dir FzfCustomFiles
\ call fzf#vim#files(<q-args>, {'options': ['--layout=reverse', '--info=inline', '--preview', 'cat {}']}, <bang>0)
" General Key Bindings
" ==============================================================================
" Telescope Bindings
nnoremap <leader>h <cmd>FzfHistory<cr>
nnoremap <leader>f <cmd>FzfCustomFiles<cr>
nnoremap <leader>g <cmd>FzfCustomRG<cr>
nnoremap <leader>tt <cmd>FzfTags<cr>
nnoremap <leader>tb <cmd>FzfBTags<cr>
nnoremap <leader>cc <cmd>FzfCommits<cr>
nnoremap <leader>cb <cmd>FzfBCommits<cr>
nnoremap <leader>b <cmd>FzfBuffers<cr>
" Map Ctrl+HJKL to navigate buffer window
nmap <silent> <C-h> :wincmd h<CR>
nmap <silent> <C-j> :wincmd j<CR>
nmap <silent> <C-k> :wincmd k<CR>
nmap <silent> <C-l> :wincmd l<CR>
" Move by wrapped lines instead of line numbers
nnoremap j gj
nnoremap k gk
nnoremap gj j
nnoremap gk k
" Map NERDTree to Ctrl-N
map <C-n> :NERDTreeToggle<CR>
" Change to current buffer's directory
nmap cd :cd <C-R>=expand("%:p:h")<CR><CR>
" Buffer Splitting
nnoremap <leader>s :vs<CR>
" Go to next error
" Go to previous error
nnoremap <A-j> :cn<CR>
nnoremap <A-k> :cp<CR>
" Clang Format
" ==============================================================================
map <C-I> :py3file ~/clang-format.py<CR>
" Compiler Error Formats
" ==============================================================================
" Error message formats thanks to
" https://forums.handmadehero.org/index.php/forum?view=topic&catid=4&id=704#3982
set errorformat+=\\\ %#%f(%l\\\,%c):\ %m " MSVC: MSBuild
set errorformat+=\\\ %#%f(%l)\ :\ %#%t%[A-z]%#\ %m " MSVC: cl.exe
set errorformat+=\\\ %#%t%nxx:\ %m " MSVC: cl.exe, fatal errors is crudely implemented
set errorformat+=\\\ %#LINK\ :\ %m " MSVC: link.exe, can't find link library badly implemented
set errorformat+=\\\ %#%s\ :\ error\ %m " MSVC: link.exe, errors is badly implemented
set errorformat+=\\\ %#%s\ :\ fatal\ error\ %m " MSVC: link.exe, fatal errors is badly implemented
set errorformat+=\\\ %#%f(%l\\\,%c-%*[0-9]):\ %#%t%[A-z]%#\ %m " MSVC: HLSL fxc.exe
set errorformat+=%\\%%(CTIME%\\)%\\@=%m " ctime.exe -stats
" Vim Dispatch
" ==============================================================================
let s:running_windows = has("win16") || has("win32") || has("win64")
if s:running_windows
set makeprg=build
nnoremap <f5> :Make ./build.bat<cr>
else
" Set vim terminal to enter normal mode using escape like normal vim behaviour
tnoremap <Esc> <C-\><C-n>
nnoremap <f5> :Make ./build.sh<cr>
set makeprg=./build.sh
endif

735
devenver.py Normal file
View File

@ -0,0 +1,735 @@
#!/usr/bin/env python3
# DEVenver
# ------------------------------------------------------------------------------
# A simple python script to download portable applications and install them by
# unzipping them to a structured directory tree.
import urllib.request
import urllib.parse
import pathlib
import os
import tempfile
import hashlib
import shutil
import subprocess
import pprint
import argparse
import json
import importlib
from string import Template
from enum import Enum
# Internal
# ------------------------------------------------------------------------------
DOWNLOAD_CHUNK_SIZE = 1 * 1024 * 1024 # 1 megabyte
IS_WINDOWS = os.name == "nt"
script_dir = os.path.dirname(os.path.abspath(__file__))
default_base_dir = script_dir
default_base_downloads_dir = os.path.join(default_base_dir, 'Downloads')
default_base_install_dir = os.path.join(default_base_dir, 'Install')
# Arguments
# ------------------------------------------------------------------------------
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--downloads-dir',
help=f'Set the directory where downloaded files are cached (default: {default_base_downloads_dir})',
default=default_base_downloads_dir,
type=pathlib.Path)
arg_parser.add_argument('--install-dir',
help=f'Set the directory where downloaded files are installed (default: {default_base_install_dir})',
default=default_base_install_dir,
type=pathlib.Path)
arg_parser.add_argument('--manifest-file',
help=f'Python file that has a get_manifest() function returning a dictionary of applications to download & install (see manifest.py for starters)',
required=True,
type=pathlib.Path)
arg_parser.add_argument('--version',
action='version',
version='DEVenver v1')
args = arg_parser.parse_args()
base_downloads_dir = args.downloads_dir
base_install_dir = args.install_dir
# ------------------------------------------------------------------------------
# This app list must always be installed, they provide the tools to install all
# other archives. Upon installation, we will collect the installation executable
# path and store them in global variables for the rest of the progam to use to
# unzip the files.
internal_app_list = []
internal_app_list.append({
'label': '7zip',
'manifests': [],
})
version = "920"
internal_app_list[-1]['manifests'].append({ # Download the bootstrap 7zip, this can be unzipped using shutils
'download_checksum': '2a3afe19c180f8373fa02ff00254d5394fec0349f5804e0ad2f6067854ff28ac',
'download_url': f'https://www.7-zip.org/a/7za{version}.zip',
'version': version,
'executables': [
{
'path': '7za.exe',
'symlink': [],
'add_to_devenv_path': False,
'checksum': 'c136b1467d669a725478a6110ebaaab3cb88a3d389dfa688e06173c066b76fcf'
}
],
'add_to_devenv_script': [],
})
version = "2201"
internal_app_list[-1]['manifests'].append({ # Download proper 7zip, extract this exe with the bootstrap 7zip
'download_checksum': 'b055fee85472921575071464a97a79540e489c1c3a14b9bdfbdbab60e17f36e4',
'download_url': f'https://www.7-zip.org/a/7z{version}-x64.exe',
'version': version,
'executables': [
{
'path': '7z.exe',
'symlink': [],
'add_to_devenv_path': True,
'checksum': '254cf6411d38903b2440819f7e0a847f0cfee7f8096cfad9e90fea62f42b0c23'
}
],
'add_to_devenv_script': [],
})
# ------------------------------------------------------------------------------
version = "1.5.2"
internal_app_list.append({
"label": "zstd",
"manifests": [
{
"download_checksum": "68897cd037ee5e44c6d36b4dbbd04f1cc4202f9037415a3251951b953a257a09",
"download_url": f"https://github.com/facebook/zstd/releases/download/v{version}/zstd-v{version}-win64.zip",
"version": version,
"executables": [
{
"path": "zstd.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "f14e78c0651851a670f508561d2c5d647da0ba08e6b73231f2e7539812bae311",
},
],
"add_to_devenv_script": [],
},
],
})
# ------------------------------------------------------------------------------
# These variables are set once they are downloaded dynamically and installed
# from the internal app listing!
zstd_exe = ""
zip7_exe = ""
zip7_bootstrap_exe = ""
# Functions
# ------------------------------------------------------------------------------
def print_header(title):
line = f'> ' + title + ' ';
print(line.ljust(100, '-'))
def lprint(*args, level=0, **kwargs):
print(' ' + (' ' * 2 * level), *args, **kwargs)
def lexit(*args, level=0, **kwargs):
print(' ' + (' ' * 2 * level), *args, **kwargs)
exit()
def verify_file_sha256(file_path, checksum, label):
if os.path.isfile(file_path) == False:
exit(f'Cannot verify SHA256, path is not a file [path={file_path}]')
result = False
try:
file = open(file_path, 'r+b')
hasher = hashlib.sha256()
hasher.update(file.read())
derived_checksum = hasher.hexdigest()
result = derived_checksum == checksum
if result:
lprint(f'- {label} SHA256 is good: {checksum}', level=1)
else:
lprint(f'- {label} SHA256 mismatch', level=1)
lprint(f' Expect: {checksum}', level=1)
lprint(f' Actual: {derived_checksum}', level=1)
except PermissionError as exception:
lprint(f"- {label} cannot verify SHA256 due to permission error, skipping", level=1)
result = True
else:
file.close()
return result
def download_file_at_url(url, download_path, download_checksum, label):
# Check if file already downloaded and hashes match
# --------------------------------------------------------------------------
file_already_downloaded = False
if os.path.isfile(download_path):
lprint(f'- Cached archive found: {download_path}', level=1)
file_already_downloaded = verify_file_sha256(download_path, download_checksum, 'Cached archive')
else:
lprint(f'- Download to disk: {download_path}', level=1)
lprint(f' URL: {url}', level=1)
# Download the file from URL
# --------------------------------------------------------------------------
if file_already_downloaded == False:
lprint('Initiating download request ...', level=1)
with urllib.request.urlopen(url) as response:
temp_file = tempfile.mkstemp(text=False)
temp_file_handle = temp_file[0]
temp_file_path = temp_file[1]
temp_file_io = os.fdopen(temp_file_handle, mode='w+b')
download_failed = False
try:
line = ''
total_download_size = int(response.getheader('Content-Length'))
bytes_downloaded = 0
while chunk := response.read(DOWNLOAD_CHUNK_SIZE):
bytes_written = temp_file_io.write(chunk)
bytes_downloaded += bytes_written
percent_downloaded = int(bytes_downloaded / total_download_size * 100)
lprint(' ' * len(line), end='\r', level=1)
line = f'Downloading {percent_downloaded:.2f}% ({bytes_downloaded}/{total_download_size})'
lprint(line, end='\r', level=1)
except Exception as exception:
download_failed = True
lprint(f'Download {label} from {url} failed, {exception}', level=1)
finally:
temp_file_io.close()
print()
if download_failed == True:
os.remove(temp_file_path)
exit()
os.rename(temp_file_path, download_path)
if file_already_downloaded == False:
if verify_file_sha256(download_path, download_checksum, 'Downloaded archive') == False:
exit()
class UnzipMethod(Enum):
SHUTILS = 0
ZIP7_BOOTSTRAP = 1
DEFAULT = 2
def get_exe_install_dir(install_dir, label, version_label):
result = pathlib.Path(install_dir, label.replace(' ', '_'), version_label)
return result
def get_exe_install_path(install_dir, label, version_label, exe_rel_path):
install_dir = get_exe_install_dir(install_dir, label, version_label)
result = pathlib.Path(install_dir, exe_rel_path)
return result
def get_exe_symlink_dir(install_dir):
result = pathlib.Path(install_dir, "Symlinks")
return result
def download_and_install_archive(download_url,
download_checksum,
exe_list,
version_label,
label,
unzip_method,
download_dir,
install_dir):
exe_install_dir = get_exe_install_dir(install_dir=install_dir,
label=label,
version_label=version_label)
# Evaluate if we have already installed the requested archive
# --------------------------------------------------------------------------
exes_are_not_a_file = []
exes_missing = []
exes_present = []
for exe_dict in exe_list:
exe_path = get_exe_install_path(install_dir=install_dir,
label=label,
version_label=version_label,
exe_rel_path=exe_dict['path'])
if os.path.exists(exe_path) == True:
if os.path.isfile(exe_path) == True:
exes_present.append(exe_dict)
else:
exes_are_not_a_file.append(exe_dict)
else:
exes_missing.append(exe_dict)
# Executables not install yet, verify, download and install if possible
# --------------------------------------------------------------------------
if len(exes_present) != len(exe_list):
# Check if any of the manifest files are not files
# ----------------------------------------------------------------------
if len(exes_are_not_a_file) > 0: # Some item exists at the path but they are not files
lprint(f'- {label} is installed but some of the expected executables are not a file!', level=1)
for exe_dict in exes_are_not_a_file:
lprint(f' {exe_dict["path"]}', level=1)
lprint(f' Installation cannot proceed as unpacking would overwrite these paths', level=1)
return
# Check if any files are missing
# ----------------------------------------------------------------------
# Some executables are missing, some are available, installation will
# trample over existing files, its not safe to unzip the archive as we may
# overwrite config files or some other files that have been modified by the
# program.
#
# Note that all files missing means we can assume that we haven't installed
# yet ..
if len(exes_missing) > 0 and len(exes_missing) != len(exe_list):
lprint(f'- {label} is installed but some of the expected executables are missing from the installation!', level=1)
for exe_dict in exes_are_not_a_file:
lprint(f' {exe_dict["path"]}', level=1)
lprint(f' Installation cannot proceed as unpacking could delete ', level=1)
return
assert(len(exes_missing) == len(exe_list))
assert(len(exes_present) == 0)
# Not installed yet, download and install
# ----------------------------------------------------------------------
# Determine the file name we are downloading from the URL
download_url_parts = urllib.parse.urlparse(download_url)
download_name = pathlib.Path(urllib.parse.unquote(download_url_parts.path))
# The path to move the temp file to after successful download, e.g.
# download_dir = C:/Dev/Downloads/Wezterm-windows-{version}.zip
# download_name = Wezterm-windows-{version}.zip
download_path = pathlib.Path(download_dir, download_name.name)
# Download the archive at the URL
download_file_at_url(download_url, download_path, download_checksum, label)
# Install the archive by unpacking it
# ----------------------------------------------------------------------
if unzip_method == UnzipMethod.SHUTILS:
lprint(f'- SHUtils unzip install {label} to: {exe_install_dir}', level=1)
shutil.unpack_archive(download_path, exe_install_dir, 'zip')
else:
command = ''
if unzip_method == UnzipMethod.ZIP7_BOOTSTRAP:
command = f'"{zip7_bootstrap_exe}" x -bd "{download_path}" -o"{exe_install_dir}"'
lprint(f'- 7z (bootstrap) unzip {label} to: {exe_install_dir}', level=1)
lprint(f' Command: {command}', level=1)
subprocess.run(command)
else:
archive_path = download_path
intermediate_zip_file_extracted = False
# We could have a "app.zst" situation or an "app.tar.zst" situation
#
# "app.zst" only needs 1 extraction from the zstd tool
# "app.tar.zst" needs 1 zstd extract and then 1 7zip extract
#
# When we have "app.tar.zst" we extract to the install folder, e.g.
#
# "app/1.0/app.tar"
#
# We call this an intermediate zip file, we will extract that file
# with 7zip. After we're done, we will delete that _intermediate_
# file to cleanup our install directory.
if archive_path.suffix == '.zst':
archive_without_suffix = pathlib.Path(str(archive_path)[:-len(archive_path.suffix)]).name
next_archive_path = pathlib.Path(exe_install_dir, archive_without_suffix)
if os.path.exists(next_archive_path) == False:
command = f'"{zstd_exe}" --output-dir-flat "{exe_install_dir}" -d "{archive_path}"'
lprint(f'- zstd unzip {label} to: {exe_install_dir}', level=1)
lprint(f' Command: {command}', level=1)
os.makedirs(exe_install_dir)
subprocess.run(command)
# Remove the extension from the file, we just extracted it
archive_path = next_archive_path
# If there's still a suffix after we removed the ".zst" we got
# an additional archive to unzip, e.g. "app.tar" remaining.
intermediate_zip_file_extracted = len(archive_path.suffix) > 0
if len(archive_path.suffix) > 0:
command = f'"{zip7_exe}" x -aoa -spe -bso0 "{archive_path}" -o"{exe_install_dir}"'
command = command.replace('\\', '/')
lprint(f'- 7z unzip install {label} to: {exe_install_dir}', level=1)
lprint(f' Command: {command}', level=1)
subprocess.run(command)
if intermediate_zip_file_extracted:
lprint(f'- Detected intermediate zip file in install root, removing: {archive_path}', level=1)
os.remove(archive_path)
# Remove duplicate root folder if detected
# ----------------------------------------------------------------------
# If after unpacking, there's only 1 directory in the install direction, we
# assume that the zip contains a root folder. We will automatically merge
# the root folder to the parent.
has_files_in_install_dir = False
dir_count = 0
dupe_root_folder_name = ''
with os.scandir(exe_install_dir) as scan_handle:
for it in list(scan_handle):
if it.is_file():
has_files_in_install_dir = True
break
elif it.is_dir():
dupe_root_folder_name = it.name
dir_count += 1
if dir_count > 1:
break
if dir_count == 1 and not has_files_in_install_dir:
# There is only one folder after we unzipped, what happened here is
# that the archive we unzipped had its contents within a root
# folder. We will pull those files out because we already unzipped
# into an isolated location for the application, e.g.
#
# Our install location C:/Dev/Install/7zip/920
# After unzip C:/Dev/Install/7zip/920/7zip-920-x64
#
# We have an duplicate '7zip-920-x64' directory in our
# installation. Move all the files in the duplicate directory up to
# our '920' folder then remove the duplicate folder.
dupe_root_folder_path = pathlib.Path(exe_install_dir, dupe_root_folder_name)
lprint(f'- Detected duplicate root folder after unzip: {dupe_root_folder_path}', level=1)
lprint(f' Merging duplicate root folder to parent: {exe_install_dir}', level=1)
for file_name in os.listdir(dupe_root_folder_path):
src = pathlib.Path(dupe_root_folder_path, file_name)
dest = pathlib.Path(exe_install_dir, file_name)
shutil.move(src, dest)
os.rmdir(dupe_root_folder_path)
# Verify the installation by checking the SHA256 of the executables
# --------------------------------------------------------------------------
exes_with_bad_hashes = []
for exe_dict in exe_list:
exe_rel_path = exe_dict['path']
exe_path = get_exe_install_path(install_dir=install_dir,
label=label,
version_label=version_label,
exe_rel_path=exe_rel_path)
if os.path.isfile(exe_path) == False:
lexit(f'- Installed {label} but could not find expected file for validating install: {exe_path}', level=1)
if verify_file_sha256(file_path=exe_path, checksum=exe_dict['checksum'], label=exe_rel_path) == False:
exes_with_bad_hashes.append(exe_dict)
if len(exes_with_bad_hashes) > 0:
lprint(f'- {label} is installed but executable SHA256 does not match!', level=1)
lprint(f' See hashes above, executable path(s):', level=1)
for exe_dict in exes_with_bad_hashes:
lprint(f' {exe_dict["path"]}', level=1)
lprint(f' Something has modified the executable, this may be malicious or not!', level=1)
lprint(f' Manually uninstall the existing installation or amend the binary to be', level=1)
lprint(f' able to continue. Exiting.', level=1)
exit()
else:
lprint(f'- {label} installed and valid: {exe_install_dir}', level=1)
# Do the symlinks
# --------------------------------------------------------------------------
symlink_dir = get_exe_symlink_dir(install_dir)
paths_to_add_to_devenv_script = set()
for exe_dict in exe_list:
exe_rel_path = exe_dict['path']
exe_path = get_exe_install_path(install_dir=install_dir,
label=label,
version_label=version_label,
exe_rel_path=exe_rel_path)
for symlink_entry in exe_dict["symlink"]:
symlink_dest = symlink_dir / symlink_entry
symlink_src = exe_path
if os.path.exists(symlink_dest):
# Windows uses hardlinks because symlinks require you to enable "developer" mode
# Everyone else uses symlinks
if (IS_WINDOWS and not os.path.isfile(symlink_dest)) or (not IS_WINDOWS and not os.path.islink(symlink_dest)):
lprint( "- Cannot create symlink! The destionation file to create the symlink at.", level=1)
lprint( " already exists and is *not* a link. We cannot remove this safely as we", level=1)
lprint( " don't know what it is, exiting.", level=1)
lprint(f" Symlink Source: {symlink_src}", level=1)
lexit (f" Symlink Dest: {symlink_dest}", level=1)
os.unlink(symlink_dest)
if IS_WINDOWS == True:
os.link(src=symlink_src, dst=symlink_dest)
else:
os.symlink(src=symlink_src, dst=symlink_dest)
# Collect paths to add to the devenv script
# ----------------------------------------------------------------------
if exe_dict['add_to_devenv_path'] == True:
path = exe_path.parent.relative_to(install_dir)
paths_to_add_to_devenv_script.add(path)
global devenv_script_buffer
for path in paths_to_add_to_devenv_script:
if IS_WINDOWS:
devenv_script_buffer += f"set PATH=\"%~dp0{path}\";%PATH%\n"
else:
devenv_script_buffer += f"PATH=\"$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd ){path}\";%PATH%\n"
# Search the 2 dictionarries, 'first' and 'second' for the key. A matching key
# in 'first' taking precedence over the 'second' dictionary. If no key is
# found in either dictionaries then this function
# returns an empty string.
class ValidateAppListResult:
def __init__(self):
self.app_count = 0
def validate_app_list(app_list):
result = ValidateAppListResult()
manifest_rule_table = {
'download_checksum': 'manifest must specify the SHA256 checksum for the downloaded file',
'version': 'manifest must specify the app version that is to be installed',
'executables': 'manifest must specify an array of executable(s) for verifying installation',
'download_url': 'manifest must specify the URL to download the app from',
'add_to_devenv_script': 'manifest must specify an array of strings to inject into the portable development environment setup script',
}
executable_rule_table = {
'path': 'executables must specify a path to a file from the installation to verify its checksum',
'symlink': 'executables must specify an array of symlink names that will target the path',
'add_to_devenv_path': 'executables must specify an boolean to indicate if the executable path should be added to the environment path',
'checksum': 'executables must specify a string with the checksum of the executable',
}
for app in app_list:
manifest_list = app['manifests']
result.app_count += len(manifest_list)
# Verify the label
# ----------------------------------------------------------------------
label = app.get('label', '')
if 'label' not in app:
exit('Label missing from application list, app must have a label specified, e.g. { "label": "App Name", "manifests": [] }')
# Verify that the mandatory keys are in the manifest
# ----------------------------------------------------------------------
for manifest in manifest_list:
for key in manifest_rule_table:
value = manifest.get(key, "")
if key.startswith("add_to_devenv"):
if not isinstance(value, list):
exit(f'{label} error: {key} in manifest must be an array to proceed\n{pprint.pformat(app)}')
elif key == "executables":
for executable in value:
for executable_key in executable_rule_table:
executable_value = executable.get(executable_key, "")
if executable_key == "path":
if not isinstance(executable_value, str) or len(executable_value) == 0:
exit(f'{label} error: required key "{executable_key}" is invalid, {executable_rule_table[executable_key]}\n{pprint.pformat(app)}')
elif executable_key == "symlink":
if not isinstance(executable_value, list):
exit(f'{label} error: required key "{executable_key}" is invalid, {executable_rule_table[executable_key]}\n{pprint.pformat(app)}')
elif executable_key == "add_to_devenv_path":
if not isinstance(executable_value, bool):
exit(f'{label} error: required key "{executable_key}" is invalid, {executable_rule_table[executable_key]}\n{pprint.pformat(app)}')
elif executable_key == "checksum":
if not isinstance(executable_value, str):
exit(f'{label} error: required key "{executable_key}" is invalid, {executable_rule_table[executable_key]}\n{pprint.pformat(app)}')
elif len(value) == 0:
exit(f'{label} error: required key "{key}" is missing/empty, {manifest_rule_table[key]}\n{pprint.pformat(app)}')
return result
devenv_script_buffer = """@echo off
"""
def install_app_list(app_list, download_dir, install_dir):
title = "Internal Apps" if app_list is internal_app_list else "User Apps"
print_header(title)
result = {}
validate_app_list_result = validate_app_list(app_list)
app_index = 0
for app in app_list:
manifest_list = app['manifests']
for manifest in manifest_list:
app_index += 1
# Extract variables from manifest
# ------------------------------------------------------------------
label = app["label"]
download_checksum = manifest['download_checksum']
version = manifest["version"]
download_url = manifest["download_url"]
exe_list = manifest['executables']
unzip_method = UnzipMethod.DEFAULT
# Bootstrapping code, when installing the internal app list, we will
# assign the variables to point to our unarchiving tools.
# ------------------------------------------------------------------
if app_list is internal_app_list:
global zip7_exe
global zip7_bootstrap_exe
global zstd_exe
exe_path = get_exe_install_path(install_dir, label, version, manifest['executables'][0]['path'])
if label == '7zip':
if version == '920':
unzip_method = UnzipMethod.SHUTILS
zip7_bootstrap_exe = exe_path
else:
unzip_method = UnzipMethod.ZIP7_BOOTSTRAP
zip7_exe = exe_path
if label == 'zstd':
zstd_exe = exe_path
# Download and install
# ------------------------------------------------------------------
lprint(f'[{app_index:03}/{validate_app_list_result.app_count:03}] Setup {label} v{version}', level=0)
download_and_install_archive(download_url=download_url,
download_checksum=download_checksum,
exe_list=exe_list,
version_label=version,
label=label,
unzip_method=unzip_method,
download_dir=download_dir,
install_dir=install_dir)
# Post-installation
# ------------------------------------------------------------------
# Collate results into results
if label not in result:
result.update({label: []})
for item in exe_list:
app_install_dir = get_exe_install_dir(install_dir, label, version)
app_exe_path = get_exe_install_path(install_dir, label, version, item['path'])
app_exe_dir = pathlib.Path(app_exe_path).parent
# Add executable into the result list
result[label].append({
'version': version,
'install_dir': app_install_dir,
'exe_path': app_exe_path,
})
# Add the snippets verbatim specified in the manifest
global devenv_script_buffer
for line in manifest['add_to_devenv_script']:
devenv_script_buffer += (line + '\n')
if app_list is internal_app_list:
if len(str(zip7_exe)) == 0 or len(str(zip7_bootstrap_exe)) == 0 or len(str(zstd_exe)) == 0:
exit("Internal app list did not install 7zip bootstrap, 7zip or zstd, we are unable to install archives\n"
f" - zip7_bootstrap_exe: {zip7_bootstrap_exe}\n"
f" - zip7_exe: {zip7_exe}\n"
f" - zstd_exe: {zstd_exe}\n")
return result
def run(user_app_list,
download_dir=base_downloads_dir,
install_dir=base_install_dir):
""" Download and install the given user app list at the specified
directories. The apps given must be archives that can be unpacked for
installation (e.g. portable distributions).
Parameters:
user_app_list (list): A list of dictionaries that contain app and
manifest information dictating what is to be installed via this
function.
download_dir (string): The path that intermediate downloaded files will
be kept at.
install_dir (string): The path that installed applications will be
unpacked to
Returns:
result (list): A list of dictionaries containing the install locations
of each app, e.g.
"""
# Run
# --------------------------------------------------------------------------
# Create the starting directories and install the internal app list (e.g.
# 7zip) which will be used to unzip-install the rest of the apps in the user
# app list.
#
# To do this without dependencies, we first download an old version of 7zip,
# version 9.20 which is distributed as a .zip file which Python can natively
# unzip.
#
# We then use the old version of 7zip and download a newer version of 7zip
# and extract it using the bootstrap-ed version. As of writing this, 7zip
# does not release a portable distribution of itself yet, instead what we do
# is download the installer and extract it ourselves using the bootstrap
# 7zip.
# Create the paths requested by the user
os.makedirs(download_dir, exist_ok=True)
os.makedirs(install_dir, exist_ok=True)
os.makedirs(pathlib.Path(install_dir, "Symlinks"), exist_ok=True)
for path in [download_dir, install_dir]:
if not os.path.isdir(path):
exit(f'Path "{path}" is not a directory, script can not proceed. Exiting.')
# Validate all the manifests before starting
internal_app_validate_result = validate_app_list(internal_app_list)
user_app_validate_result = validate_app_list(user_app_list)
# Install apps
internal_apps = install_app_list(app_list=internal_app_list,
download_dir=download_dir,
install_dir=install_dir)
user_apps = install_app_list(app_list=user_app_list,
download_dir=download_dir,
install_dir=install_dir)
# Write the devenv script with environment variables
global devenv_script_buffer
devenv_script_buffer += "set PATH=\"%~dp0Symlinks\";%PATH%\n"
devenv_script_name = "devenv.bat" if IS_WINDOWS else "devenv.sh"
devenv_script_path = pathlib.Path(install_dir, devenv_script_name)
lprint(f"Writing script to augment the environment with installed applications: {devenv_script_path}")
with open(devenv_script_path, 'w') as file:
file.write(devenv_script_buffer)
# Merge the install dictionaries, this dictionary contains
# (app label) -> [array of installed versions]
result = internal_apps
for key, value in user_apps.items():
if key not in result:
result.update({key: value})
else:
result[key] += value
return result
if __name__ == '__main__':
run()

976
devenver_manifest.py Normal file
View File

@ -0,0 +1,976 @@
def get_manifest():
result = []
# --------------------------------------------------------------------------
version = "20221119-145034-49b9839f"
result.append({
"label": "WezTerm",
"manifests": [
{
"version": version,
"download_checksum": "7041d2c02d226c0c051cc9f6373d51ac9a2de00025e18582077c76e8ad68abe1",
"download_url": f"https://github.com/wez/wezterm/releases/download/{version}/WezTerm-windows-{version}.zip",
"executables": [
{
"path": "wezterm-gui.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "e3faa247d69a8a966302a2ab4e655b08b79548707db79a7b724cf18cccf5ae35",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "2.304"
result.append({
"label": "JetBrains_Mono_Font",
"manifests": [
{
"download_checksum": "6f6376c6ed2960ea8a963cd7387ec9d76e3f629125bc33d1fdcd7eb7012f7bbf",
"version": "2.304",
"download_url": f"https://download.jetbrains.com/fonts/JetBrainsMono-{version}.zip",
"executables": [
{
"path": "fonts/ttf/JetBrainsMono-Regular.ttf",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "a0bf60ef0f83c5ed4d7a75d45838548b1f6873372dfac88f71804491898d138f",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
result.append({
"label": "CMake",
"manifests": [],
})
version = "3.23.4"
result[-1]['manifests'].append({
"download_checksum": "df15113aaab9e5f8cac254e02cf23f70d02407c9bf2983c82a9fe0d35bd20682",
"download_url": f"https://github.com/Kitware/CMake/releases/download/v{version}/cmake-{version}-windows-x86_64.zip",
"version": "3.23.4",
"executables": [
{
"path": "bin/cmake.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "426074cd812586551fbab2bde67377113e2085c78c2e9a887748e85b4dc3dda5",
}
],
"add_to_devenv_script": [],
})
version = "3.10.3"
result[-1]['manifests'].append({
"download_checksum": "3bd57d1cfcf720a4cc72db77bda4c76a7b700fb0341821ad868963ad28856cd0",
"download_url": f"https://github.com/Kitware/CMake/releases/download/v{version}/cmake-{version}-win64-x64.zip",
"version": version,
"executables": [
{
"path": "bin/cmake.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "f2e3b486d87d2a6bc19b3a62c740028f3f8945875196ac7d3d0e69649e98730a",
}
],
"add_to_devenv_script": [],
})
# --------------------------------------------------------------------------
version = "1.9.4"
result.append({
"label": "Doxygen",
"manifests": [
{
"download_checksum": "3b34098c5fb016baa1d29aba101fe9d6843213b966b92a6b12c8856c547ee0c4",
"download_url": f"https://github.com/doxygen/doxygen/releases/download/Release_{version.replace('.', '_')}/doxygen-{version}.windows.x64.bin.zip",
"version": version,
"executables": [
{
"path": "doxygen.exe",
"symlink": [f"doxygen-{version}.exe"],
"add_to_devenv_path": True,
"checksum": "3cb4d89f2b3db7eec2b6797dc6b49cdfe9adda954575898895260f66f312d730",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "2.39.1"
label = "Git"
result.append({
"label": f"{label}",
"manifests": [
{
"download_checksum": "b898306a44084b5fa13b9a52e06408d97234389d07ae41d9409bdf58cad3d227",
"download_url": f"https://github.com/git-for-windows/git/releases/download/v{version}.windows.1/PortableGit-{version}-64-bit.7z.exe",
"version": version,
"executables": [
{
"path": "cmd/git.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "2fc6d5be237efb6b429d8f40975f1a1cfe3bcac863d9335e24096c8b0ec38105",
}
],
"add_to_devenv_script": [
f"set PATH=\"%~dp0{label}\\{version}\\mingw64\\bin\";%PATH%",
f"set PATH=\"%~dp0{label}\\{version}\\usr\\bin\";%PATH%",
],
}
],
})
# --------------------------------------------------------------------------
result.append({
"label": "GCC_MinGW_AArch64",
"manifests": [],
})
arch = "aarch64-none-elf"
version = "12.2.0"
result[-1]['manifests'].append({
"download_checksum": "729e8af6aecd85cce63435b94c310c01983091b5db54842cd6604298f29d047f",
"download_url": f"https://github.com/mmozeiko/build-gcc-arm/releases/download/gcc-v{version}/gcc-v{version}-{arch}.7z",
"version": version,
"executables": [
{
"path": f"bin/{arch}-g++.exe",
"checksum": "a26baffa86bc3401790d682f13f9b321ea56153eae7dd4f332bde40a6b76fcb3",
"symlink": [f"{arch}-g++-{version}.exe"],
"add_to_devenv_path": True,
},
{
"path": f"bin/{arch}-gcc.exe",
"checksum": "0f594c7e741207f1613aa958369b12fab781741718688710b7082cac172fadf5",
"symlink": [f"{arch}-gcc-{version}.exe"],
"add_to_devenv_path": True,
},
],
"add_to_devenv_script": [],
})
version = "11.3.0"
result[-1]['manifests'].append({
"download_checksum": "a000bdeeb225145a1450c1b9b1094ef71c13fc4de2ab300a65acbf51cd107c7d",
"download_url": f"https://github.com/mmozeiko/build-gcc-arm/releases/download/gcc-v{version}/gcc-v{version}-{arch}.7z",
"version": version,
"executables": [
{
"path": f"bin/{arch}-g++.exe",
"checksum": "47eaef0e603c9fcae18f2efada305888503e878053119ede3a9e0b8b8beac2ee",
"symlink": [f"{arch}-g++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/{arch}-gcc.exe",
"checksum": "205d0b05d64bc80908deb5a64e5f3bf8769cfc08b272835f97aaeaec13ccd533",
"symlink": [f"{arch}-gcc-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
version = "10.3.0"
result[-1]['manifests'].append({
"download_checksum": "095ab5a12059fa5dc59f415c059eb577f443a766eb1dd312fbede0f59940f432",
"download_url": f"https://github.com/mmozeiko/build-gcc-arm/releases/download/gcc-v{version}/gcc-v{version}-{arch}.7z",
"version": version,
"executables": [
{
"path": f"bin/{arch}-g++.exe",
"checksum": "f2b2d3c6dab0f84a151835540f25e6d6f9442d00bf546bc4c709fad4b6fdda06",
"symlink": [f"{arch}-g++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/{arch}-gcc.exe",
"checksum": "95a8478ecb5133029f3058fb0207f19ee00157a6dd81f220e8308305f0e25fe8",
"symlink": [f"{arch}-gcc-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
# --------------------------------------------------------------------------
result.append({
"label": "GCC_MinGW_ARM",
"manifests": [],
})
arch = "arm-none-eabi"
version = "12.2.0"
result[-1]['manifests'].append({
"download_checksum": "aa581b3a5d446bb2d9827f2ea1f02b066b6713d4543d24abbd3181f626036c39",
"download_url": f"https://github.com/mmozeiko/build-gcc-arm/releases/download/gcc-v{version}/gcc-v{version}-{arch}.7z",
"version": version,
"executables": [
{
"path": f"bin/{arch}-g++.exe",
"checksum": "fa48985c43cf82b426c461381e4c50d0ac3e9425f7e97bf116e1bab4b3a2a388",
"symlink": [f"{arch}-g++-{version}.exe"],
"add_to_devenv_path": True,
},
{
"path": f"bin/{arch}-gcc.exe",
"checksum": "94a342aae99cae1a95f3636bb5c7b11f5e17015aee98b556989944ec38755be2",
"symlink": [f"{arch}-gcc-{version}.exe"],
"add_to_devenv_path": True,
},
],
"add_to_devenv_script": [],
})
version = "11.3.0"
result[-1]['manifests'].append({
"download_checksum": "797ed71f60fae386c8875bb4e75e244afb15ded9e00ac77b6670a62be7614cc6",
"download_url": f"https://github.com/mmozeiko/build-gcc-arm/releases/download/gcc-v{version}/gcc-v{version}-{arch}.7z",
"version": version,
"executables": [
{
"path": f"bin/{arch}-g++.exe",
"checksum": "a36f2ea6846badf7c91631f118e88967f25d6e479a9beea158445ce75403a655",
"symlink": [f"{arch}-g++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/{arch}-gcc.exe",
"checksum": "71158642a3d4921eda106a1b23640f1ed8bf1725ceaa98cbc8729a9a8115b09a",
"symlink": [f"{arch}-gcc-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
version = "10.3.0"
result[-1]['manifests'].append({
"download_checksum": "af0fc2da062aa6423a91213e231ecc5981136b9b0655837ebdbbc5ad879d2d9e",
"download_url": f"https://github.com/mmozeiko/build-gcc-arm/releases/download/gcc-v{version}/gcc-v{version}-{arch}.7z",
"version": version,
"executables": [
{
"path": f"bin/{arch}-g++.exe",
"checksum": "c3dc49b561d177b3586992dfea86067eb8799e1586a7f26cea5b0ea97926632e",
"symlink": [f"{arch}-g++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/{arch}-gcc.exe",
"checksum": "7e680ffec593474a54193f5253b620cf59b6e3a1720dd35ab95bcb53582b7b7d",
"symlink": [f"{arch}-gcc-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
# --------------------------------------------------------------------------
result.append({
"label": "GCC_MinGW",
"manifests": [],
})
version = "12.2.0"
mingw_version = "10.0.0"
result[-1]['manifests'].append({
"download_checksum": "5cbe5ea7533f6d24af3a57fe7022032f420b15d7c4e38c0d16534a42d33213a4",
"download_url": f"https://github.com/mmozeiko/build-gcc/releases/download/gcc-v{version}-mingw-v{mingw_version}/gcc-v{version}-mingw-v{mingw_version}-x86_64.7z",
"version": version,
"executables": [
{
"path": f"bin/g++.exe",
"checksum": "886b0f25256ddbd0f4ad09e6e3b81279f9a8b6a1b5c32c714c9c201d802caa39",
"symlink": [f"g++-{version}.exe"],
"add_to_devenv_path": True,
},
{
"path": f"bin/gcc.exe",
"checksum": "91c910fa5257fdfd0291c347c81a73c7facb1f486dba941f977714672895c96e",
"symlink": [f"gcc-{version}.exe"],
"add_to_devenv_path": True,
},
],
"add_to_devenv_script": [],
})
version = "11.3.0"
result[-1]['manifests'].append({
"download_checksum": "e2c5c64659aeda77680c5eec80bbaa4db3f117b21febeb3f13fd76d580604fd0",
"download_url": f"https://github.com/mmozeiko/build-gcc/releases/download/gcc-v{version}-mingw-v{mingw_version}/gcc-v{version}-mingw-v{mingw_version}-x86_64.7z",
"version": version,
"executables": [
{
"path": f"bin/g++.exe",
"checksum": "e92ecfa0171f2ab0c3ca39f2121ab5e887b3a378399a4be7e056820f5841c7a5",
"symlink": [f"g++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/gcc.exe",
"checksum": "f3226120196ea37ab3e450bd0f26d816ee28556d18aa0de64c3e427f31d66eeb",
"symlink": [f"gcc-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
version = "10.3.0"
mingw_version = "8.0.0"
result[-1]['manifests'].append({
"download_checksum": "c8f38f6b1d264d7e008009bd32a04ca71b4ee3a3113e67930ab31c2e06818317",
"download_url": f"https://github.com/mmozeiko/build-gcc/releases/download/gcc-v{version}-mingw-v{mingw_version}/gcc-v{version}-mingw-v{mingw_version}-x86_64.7z",
"version": version,
"executables": [
{
"path": f"bin/g++.exe",
"checksum": "5c93b6da129ea01ee5fc87d5c7db948fc3bc62bae261ded9a883f1fa543571d2",
"symlink": [f"g++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/gcc.exe",
"checksum": "54a5f8d09e6741b9c94d1494f383c424c20449e3e06f36bf96603aeda9874405",
"symlink": [f"gcc-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
# --------------------------------------------------------------------------
result.append({
"label": "LLVM",
"manifests": [],
})
version = "15.0.7"
result[-1]['manifests'].append({
"download_checksum": "5428cb72acf63ce3bc4328e546a36674c9736ec040ecc176d362201c6548e6a8",
"download_url": f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/LLVM-{version}-win64.exe",
"version": version,
"executables": [
{
"path": f"bin/clang++.exe",
"checksum": "1f523e33de4ce9d591b4eb9bad102f086e8480488148f8db0d5c87056798ce3e",
"symlink": [f"clang++-{version}.exe"],
"add_to_devenv_path": True,
},
{
"path": f"bin/clang.exe",
"checksum": "1f523e33de4ce9d591b4eb9bad102f086e8480488148f8db0d5c87056798ce3e",
"symlink": [f"clang-{version}.exe"],
"add_to_devenv_path": True,
},
],
"add_to_devenv_script": [],
})
version = "14.0.6"
result[-1]['manifests'].append({
"download_checksum": "e8dbb2f7de8e37915273d65c1c2f2d96844b96bb8e8035f62c5182475e80b9fc",
"download_url": f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/LLVM-{version}-win64.exe",
"version": version,
"executables": [
{
"path": f"bin/clang++.exe",
"checksum": "d557b79bc09a01141ac7d940016f52ce1db081e31d7968f0d9b6f4c192d8f8cc",
"symlink": [f"clang++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/clang.exe",
"checksum": "d557b79bc09a01141ac7d940016f52ce1db081e31d7968f0d9b6f4c192d8f8cc",
"symlink": [f"clang-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
version = "13.0.1"
result[-1]['manifests'].append({
"download_checksum": "9d15be034d52ec57cfc97615634099604d88a54761649498daa7405983a7e12f",
"download_url": f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/LLVM-{version}-win64.exe",
"version": version,
"executables": [
{
"path": f"bin/clang++.exe",
"checksum": "e3f26820ac446cb7c471cce49f6646b4346aa5380d11790ceaa7bf494a94b21d",
"symlink": [f"clang++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/clang.exe",
"checksum": "e3f26820ac446cb7c471cce49f6646b4346aa5380d11790ceaa7bf494a94b21d",
"symlink": [f"clang-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
version = "12.0.1"
result[-1]['manifests'].append({
"download_checksum": "fcbabc9a170208bb344f7bba8366cca57ff103d72a316781bbb77d634b9e9433",
"download_url": f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/LLVM-{version}-win64.exe",
"version": version,
"executables": [
{
"path": f"bin/clang++.exe",
"checksum": "9f0748de7f946c210a030452de226986bab46a0121d7236ea0e7b5079cb6dfef",
"symlink": [f"clang++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/clang.exe",
"checksum": "9f0748de7f946c210a030452de226986bab46a0121d7236ea0e7b5079cb6dfef",
"symlink": [f"clang-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
version = "11.1.0"
result[-1]['manifests'].append({
"download_checksum": "b5770bbfac712d273938cd155e232afaa85c2e8d865c7ca504a104a838568516",
"download_url": f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{version}/LLVM-{version}-win64.exe",
"version": version,
"executables": [
{
"path": f"bin/clang++.exe",
"checksum": "f72591f8a02e4b7573aa2fcd2999a3ea76fe729e2468e5414853617268798dfd",
"symlink": [f"clang++-{version}.exe"],
"add_to_devenv_path": False,
},
{
"path": f"bin/clang.exe",
"checksum": "f72591f8a02e4b7573aa2fcd2999a3ea76fe729e2468e5414853617268798dfd",
"symlink": [f"clang-{version}.exe"],
"add_to_devenv_path": False,
},
],
"add_to_devenv_script": [],
})
# --------------------------------------------------------------------------
version = "1.11.1"
result.append({
"label": "Ninja",
"manifests": [
{
"download_url": f"https://github.com/ninja-build/ninja/releases/download/v{version}/ninja-win.zip",
"download_checksum": "524b344a1a9a55005eaf868d991e090ab8ce07fa109f1820d40e74642e289abc",
"version": version,
"executables": [
{
"path": "ninja.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "23e7d60c17b3fcd42d9c00d49eca3c3771b04d7ccb13e49836b06b34e20211c7",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "16.19.0"
result.append({
"label": "NodeJS",
"manifests": [
{
"download_checksum": "e07399a4a441091ca0a5506faf7a9236ea1675220146daeea3bee828c2cbda3f",
"download_url": f"https://nodejs.org/dist/v{version}/node-v{version}-win-x64.7z",
"version": version,
"executables": [
{
"path": "node.exe",
"symlink": [f"node-{version}.exe"],
"add_to_devenv_path": True,
"checksum": "e4e7f389fbec9300275defc749246c62bdbe4f66406eb01e7c9a4101e07352da",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
date = "20230116"
version = f"3.10.9+{date}"
label = "Python"
result.append({
"label": f"{label}",
"manifests": [
{
"download_checksum": "4cfa6299a78a3959102c461d126e4869616f0a49c60b44220c000fc9aecddd78",
"download_url": f"https://github.com/indygreg/python-build-standalone/releases/download/{date}/cpython-{version}-x86_64-pc-windows-msvc-shared-pgo-full.tar.zst",
"version": version,
"executables": [
{
"path": "install/python.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "6dafb845aba67aba898f5aa8adf6c48061e7ffea1d2ed7d290a1e4386e78f2f0",
}
],
"add_to_devenv_script": [
f"set PYTHONHOME=%~dp0{label}\\{version}\\install",
f"set PATH=\"%~dp0{label}\\{version}\\install\\Script\";%PATH%",
],
}
],
})
# --------------------------------------------------------------------------
version = "1.24"
result.append({
"label": "Renderdoc",
"manifests": [
{
"download_url": f"https://renderdoc.org/stable/{version}/RenderDoc_{version}_64.zip",
"download_checksum": "dbd215f7e1c7933b8eedc49499a4372c92e68ddab04af4658f434bfe6c382a9a",
"version": version,
"executables": [
{
"path": "qrenderdoc.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "cfb96468355a416568faf89db18cd8a195bccec87ea16b3fffd3cc13c952c5fd",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "0.6.1"
result.append({
"label": "Zeal",
"manifests": [
{
"download_url": f"https://github.com/zealdocs/zeal/releases/download/v{version}/zeal-portable-{version}-windows-x64.7z",
"download_checksum": "08e9992f620ba0a5ea348471d8ac9c85059e95eedd950118928be639746e3f94",
"version": version,
"executables": [
{
"path": "zeal.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "d1e687a33e117b6319210f40e2401b4a68ffeb0f33ef82f5fb6a31ce4514a423",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "0.10.1"
result.append({
"label": "Zig",
"manifests": [
{
"download_url": f"https://ziglang.org/download/{version}/zig-windows-x86_64-{version}.zip",
"download_checksum": "5768004e5e274c7969c3892e891596e51c5df2b422d798865471e05049988125",
"version": version,
"executables": [
{
"path": "zig.exe",
"symlink": [f"zig-{version}.exe"],
"add_to_devenv_path": True,
"checksum": "607c9928a24f9d2e08df1ee240ebfd15ab1eb3c14b85e02f7dad6f8c8b53fea8",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "1.4.13"
git_hash = "0066c6"
result.append({
"label": "clink",
"manifests": [
{
"download_url": f"https://github.com/chrisant996/clink/releases/download/v{version}/clink.{version}.{git_hash}.zip",
"download_checksum": "800f7657d73a00dad40d46c9317bd418172ee40cc8b3958e32fba1f0b596e829",
"version": version,
"executables": [
{
"path": "clink_x64.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "331266334f59f2c978ff8e13bbcadb218051e790b61d9cc69e85617276c51298",
}
],
"add_to_devenv_script": [
"set CLINK_PATH=%~dp0clink-completions"
],
}
],
})
# --------------------------------------------------------------------------
version = "1.11.1"
result.append({
"label": "Dependencies",
"manifests": [
{
"download_url": f"https://github.com/lucasg/Dependencies/releases/download/v{version}/Dependencies_x64_Release.zip",
"download_checksum": "7d22dc00f1c09fd4415d48ad74d1cf801893e83b9a39944b0fce6dea7ceaea99",
"version": version,
"executables": [
{
"path": "DependenciesGui.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "1737e5406128c3560bbb2bced3ac62d77998e592444f94b10cc0aa0bb1e617e6",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "1.4.1.1022"
result.append({
"label": "Everything",
"manifests": [
{
"download_url": f"https://www.voidtools.com/Everything-{version}.x64.zip",
"download_checksum": "c718bcd73d341e64c8cb47e97eb0c45d010fdcc45c2488d4a3a3c51acc775889",
"version": version,
"executables": [
{
"path": "Everything.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "9c282a47a18477af505e64b45c3609f21f13fe1f6ff289065497a1ec00f5d332",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "0.37.0"
result.append({
"label": "fzf",
"manifests": [
{
"download_url": f"https://github.com/junegunn/fzf/releases/download/{version}/fzf-{version}-windows_amd64.zip",
"download_checksum": "247bffe84ff3294a8c0a7bb96329d5e4152d3d034e13dec59dcc97d8a828000d",
"version": version,
"executables": [
{
"path": "fzf.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "c0f4b20d0602977ff3e592cac8eadf86473abed0d24e2def81239bd2e76047e8",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "1.1.41.1"
result.append({
"label": "jpegview",
"manifests": [
{
"download_url": f"https://github.com/sylikc/jpegview/releases/download/v{version}/JPEGView_{version}.7z",
"download_checksum": "7dd4b4b34b14e5fae331c7f4ebfb658be6684c70ec055cb1964642a8b45e4886",
"version": version,
"executables": [
{
"path": "JPEGView64/JPEGView.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "f4fe2308c932a5f4f41f67b0520fe1fe8a96c94169d98c83f9501e9dc84b56ad",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "22.02"
result.append({
"label": "mpc-qt",
"manifests": [
{
"download_url": f"https://github.com/sylikc/jpegview/releases/download/v{version}/JPEGView_{version}.7z",
"download_checksum": "2230c4f4de1a429ccc67e5c590efc0a86fbaffeb33a4dc5f391aa45e660b80c2",
"version": version,
"executables": [
{
"path": "mpc-qt.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "d7ee46b0d4a61a26f8acd5d5fd4da2d252d6bc80c5cab6a55db06e853f2acefb",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "0.8.2"
result.append({
"label": "nvim",
"manifests": [
{
"download_url": f"https://github.com/neovim/neovim/releases/download/v{version}/nvim-win64.zip",
"download_checksum": "e2d53c6fd4a3caefbff47765d63d1640a5a134de46623ed8e3f9bf547791c26f",
"version": version,
"executables": [
{
"path": "bin/nvim.exe",
"symlink": [],
"add_to_devenv_path": True,
"checksum": "dd8b045e9a76bea6add3e7a727387aef6996846907e061df07971329b9464faf",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "0.10.3"
result.append({
"label": "neovide",
"manifests": [
{
"download_url": f"https://github.com/neovide/neovide/releases/download/{version}/neovide-windows.zip",
"download_checksum": "ec54f811e5cb271102751694124380f4a58ae5edf99a1a267e8b070a362f8297",
"version": version,
"executables": [
{
"path": "neovide.exe",
"symlink": ["neovide.exe"],
"add_to_devenv_path": False,
"checksum": "2c1df8ec7287f927554ebd9ad5cd0da34d7e72c3384fe266080ddf612adf6e5a",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "1.26.2"
result.append({
"label": "ImHex",
"manifests": [
{
"download_url": f"https://github.com/WerWolv/ImHex/releases/download/v{version}/imhex-{version}-Windows-Portable.zip",
"download_checksum": "4f58097c3ccee88d8dff0d48da0f239af8a9d444903cc19a3369f63caa8d77e6",
"version": f"version",
"executables": [
{
"path": "imhex.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "ddd448c0d8fe71295bbcc5b52c9e9f4b06956a79572b7d634436a49728f5f341",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "22.3"
result.append({
"label": "MobaXTerm",
"manifests": [
{
"download_url": f"https://download.mobatek.net/2232022120824733/MobaXterm_Portable_v{version}.zip",
"download_checksum": "c8de508d6731f31a73f061e58942691466d1d24cfa941e642e16e0930be2fad9",
"version": version,
"executables": [
{
"path": f"MobaXTerm_Personal_{version}.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "e47cb54645a368411c5d6b6cbfa7e25980a2a674d7d0c082f5137b6e77a2f362",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "3.0.5847"
result.append({
"label": "SystemInformer",
"manifests": [
{
"download_url": f"https://github.com/winsiderss/si-builds/releases/download/{version}/systeminformer-{version}-bin.zip",
"download_checksum": "4557e58f698048e882515faac89c9c7f654247dbf4bd656ceed5c3f97afef77d",
"version": "3.0.5847",
"executables": [
{
"path": "amd64/SystemInformer.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "8a6e9dfd145e5cb8d03ec3db1b7b0163325be33e5c8fdd4126e9f8df2af2a39c",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "13.0.0"
result.append({
"label": "ripgrep",
"manifests": [
{
"download_url": f"https://github.com/BurntSushi/ripgrep/releases/download/{version}/ripgrep-{version}-x86_64-pc-windows-msvc.zip",
"download_checksum": "a47ace6f654c5ffa236792fc3ee3fefd9c7e88e026928b44da801acb72124aa8",
"version": version,
"executables": [
{
"path": "rg.exe",
"symlink": ["rg.exe"],
"add_to_devenv_path": False,
"checksum": "ab5595a4f7a6b918cece0e7e22ebc883ead6163948571419a1dd5cd3c7f37972",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "2.0.0"
result.append({
"label": "sioyek",
"manifests": [
{
"download_url": f"https://github.com/ahrm/sioyek/releases/download/v{version}/sioyek-release-windows-portable.zip",
"download_checksum": "1f4fedbb38c0dc46bbba4bb95d0d6fab39fcf3525092ac26d92c891684d2bf8d",
"version": version,
"executables": [
{
"path": "sioyek.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "6c660f0f7265fabe6d943d15d9b5c7e85f2dbcf7fecb7d2cd0639e7086b1c034",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
version = "8.6.0"
result.append({
"label": "fd",
"manifests": [
{
"download_url": f"https://github.com/sharkdp/fd/releases/download/v{version}/fd-v{version}-x86_64-pc-windows-msvc.zip",
"download_checksum": "9cff97eb1c024ed94cc76a4b2d924ab3df04b37e7430c282b8188a13f1653ebe",
"version": version,
"executables": [
{
"path": "fd.exe",
"symlink": ["fd.exe"],
"add_to_devenv_path": False,
"checksum": "a93ab08528896556ba3a6c262c8d73b275df2ce7a4138f5323f3eff414403f33",
}
],
"add_to_devenv_script": [
"set FZF_DEFAULT_OPTS=--multi --layout=reverse",
"set FZF_DEFAULT_COMMAND=fd --type f --strip-cwd-prefix --hidden --follow --exclude .git --exclude .cache --exclude .vs",
],
}
],
})
# --------------------------------------------------------------------------
version = "4_12"
result.append({
"label": "WizTree",
"manifests": [
{
"download_url": f"https://www.diskanalyzer.com/files/wiztree_{version}_portable.zip",
"download_checksum": "f6b71fc54a9bb3f277efdf8afcd45df8ddc1759533f3236437309dae7778b168",
"version": version,
"executables": [
{
"path": "wiztree64.exe",
"symlink": [],
"add_to_devenv_path": False,
"checksum": "e2157dc64629a29e1713a845e5a9e7cab89d79a7390820c1bfda05c7de989c3d",
}
],
"add_to_devenv_script": [],
}
],
})
# --------------------------------------------------------------------------
return result

371
win_portable_msvc.py Normal file
View File

@ -0,0 +1,371 @@
#!/usr/bin/env python3
# This script has been gratefully sourced from Martins Mozeiko of HMN
# https://gist.github.com/mmozeiko/7f3162ec2988e81e56d5c4e22cde9977
#
# Further modifications by https://github.com/doy-lee with the primary purpose
# of facilitating multiple versions to be stored in the same root directory
# ('Redist' in the SDK was unversioned, store it versioned like all other
# folders, skip the downloading of MSVC or the SDK if we only need one of them).
#
# Changelog
# 2023-01-28
# - Inital revision from mmozeiko
# https://gist.github.com/mmozeiko/7f3162ec2988e81e56d5c4e22cde9977/6863f19cb98b933c7535acf3d59ac64268c6bd1b
# - Add individual scripts to source variables for MSVC and Windows 10
# separately "msvc-{version}.bat" and "win-sdk-{version}.bat"
# - Add '--no-sdk' and '--no-msvc' to prevent the download and installation of
# the Windows SDK and MSVC respectively.
# - Installation used to create 'Windows Kit/10/Redist' and unpack a D3D and MBN
# folder without being versioned. These folders are now placed under
# a versioned sub-directory to preserve the binaries and allow subsequent
# side-by-side installation of other versions of the SDK.
import io
import os
import sys
import json
import shutil
import hashlib
import zipfile
import tempfile
import argparse
import subprocess
import urllib.request
from pathlib import Path
OUTPUT = Path("msvc") # output folder
# other architectures may work or may not - not really tested
HOST = "x64" # or x86
TARGET = "x64" # or x86, arm, arm64
MANIFEST_URL = "https://aka.ms/vs/17/release/channel"
def download(url):
with urllib.request.urlopen(url) as res:
return res.read()
def download_progress(url, check, name, f):
data = io.BytesIO()
with urllib.request.urlopen(url) as res:
total = int(res.headers["Content-Length"])
size = 0
while True:
block = res.read(1<<20)
if not block:
break
f.write(block)
data.write(block)
size += len(block)
perc = size * 100 // total
print(f"\r{name} ... {perc}%", end="")
print()
data = data.getvalue()
digest = hashlib.sha256(data).hexdigest()
if check.lower() != digest:
exit(f"Hash mismatch for f{pkg}")
return data
# super crappy msi format parser just to find required .cab files
def get_msi_cabs(msi):
index = 0
while True:
index = msi.find(b".cab", index+4)
if index < 0:
return
yield msi[index-32:index+4].decode("ascii")
def first(items, cond):
return next(item for item in items if cond(item))
### parse command-line arguments
ap = argparse.ArgumentParser()
ap.add_argument("--show-versions", const=True, action="store_const", help="Show available MSVC and Windows SDK versions")
ap.add_argument("--accept-license", const=True, action="store_const", help="Automatically accept license")
ap.add_argument("--msvc-version", help="Get specific MSVC version")
ap.add_argument("--sdk-version", help="Get specific Windows SDK version")
ap.add_argument("--no-msvc", const=True, action="store_const", help="Skip download and installing of msvc")
ap.add_argument("--no-sdk", const=True, action="store_const", help="Skip download and installing of Windows SDK")
args = ap.parse_args()
### get main manifest
manifest = json.loads(download(MANIFEST_URL))
### download VS manifest
vs = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Manifests.VisualStudio")
payload = vs["payloads"][0]["url"]
vsmanifest = json.loads(download(payload))
### find MSVC & WinSDK versions
packages = {}
for p in vsmanifest["packages"]:
packages.setdefault(p["id"].lower(), []).append(p)
msvc = {}
sdk = {}
for pid,p in packages.items():
if pid.startswith("Microsoft.VisualStudio.Component.VC.".lower()) and pid.endswith(".x86.x64".lower()):
pver = ".".join(pid.split(".")[4:6])
if pver[0].isnumeric():
msvc[pver] = pid
elif pid.startswith("Microsoft.VisualStudio.Component.Windows10SDK.".lower()) or \
pid.startswith("Microsoft.VisualStudio.Component.Windows11SDK.".lower()):
pver = pid.split(".")[-1]
if pver.isnumeric():
sdk[pver] = pid
if args.show_versions:
print("MSVC versions:", " ".join(sorted(msvc.keys())))
print("Windows SDK versions:", " ".join(sorted(sdk.keys())))
exit(0)
install_sdk = not args.no_sdk
install_msvc = not args.no_msvc
if args.no_sdk and args.no_msvc:
exit()
msvc_ver = args.msvc_version or max(sorted(msvc.keys()))
sdk_ver = args.sdk_version or max(sorted(sdk.keys()))
info_line = "Downloading"
if install_msvc:
if msvc_ver in msvc:
msvc_pid = msvc[msvc_ver]
msvc_ver = ".".join(msvc_pid.split(".")[4:-2])
else:
exit(f"Unknown MSVC version: f{args.msvc_version}")
info_line += f" MSVC v{msvc_ver}"
if install_sdk:
if sdk_ver in sdk:
sdk_pid = sdk[sdk_ver]
else:
exit(f"Unknown Windows SDK version: f{args.sdk_version}")
info_line += f" Windows SDK v{sdk_ver}"
print(info_line)
### agree to license
tools = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Product.BuildTools")
resource = first(tools["localizedResources"], lambda x: x["language"] == "en-us")
license = resource["license"]
if not args.accept_license:
accept = input(f"Do you accept Visual Studio license at {license} [Y/N] ? ")
if not accept or accept[0].lower() != "y":
exit(0)
OUTPUT.mkdir(exist_ok=True)
total_download = 0
### download MSVC
if install_msvc:
msvc_packages = [
# MSVC binaries
f"microsoft.vc.{msvc_ver}.tools.host{HOST}.target{TARGET}.base",
f"microsoft.vc.{msvc_ver}.tools.host{HOST}.target{TARGET}.res.base",
# MSVC headers
f"microsoft.vc.{msvc_ver}.crt.headers.base",
# MSVC libs
f"microsoft.vc.{msvc_ver}.crt.{TARGET}.desktop.base",
f"microsoft.vc.{msvc_ver}.crt.{TARGET}.store.base",
# MSVC runtime source
f"microsoft.vc.{msvc_ver}.crt.source.base",
# ASAN
f"microsoft.vc.{msvc_ver}.asan.headers.base",
f"microsoft.vc.{msvc_ver}.asan.{TARGET}.base",
# MSVC redist
#f"microsoft.vc.{msvc_ver}.crt.redist.x64.base",
]
for pkg in msvc_packages:
p = first(packages[pkg], lambda p: p.get("language") in (None, "en-US"))
for payload in p["payloads"]:
with tempfile.TemporaryFile() as f:
data = download_progress(payload["url"], payload["sha256"], pkg, f)
total_download += len(data)
with zipfile.ZipFile(f) as z:
for name in z.namelist():
if name.startswith("Contents/"):
out = OUTPUT / Path(name).relative_to("Contents")
out.parent.mkdir(parents=True, exist_ok=True)
out.write_bytes(z.read(name))
### download Windows SDK
if install_sdk:
sdk_packages = [
# Windows SDK tools (like rc.exe & mt.exe)
f"Windows SDK for Windows Store Apps Tools-x86_en-us.msi",
# Windows SDK headers
f"Windows SDK for Windows Store Apps Headers-x86_en-us.msi",
f"Windows SDK Desktop Headers x86-x86_en-us.msi",
# Windows SDK libs
f"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
f"Windows SDK Desktop Libs {TARGET}-x86_en-us.msi",
# CRT headers & libs
f"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
# CRT redist
#"Universal CRT Redistributable-x86_en-us.msi",
]
with tempfile.TemporaryDirectory() as d:
dst = Path(d)
sdk_pkg = packages[sdk_pid][0]
sdk_pkg = packages[first(sdk_pkg["dependencies"], lambda x: True).lower()][0]
msi = []
cabs = []
# download msi files
for pkg in sdk_packages:
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
msi.append(dst / pkg)
with open(dst / pkg, "wb") as f:
data = download_progress(payload["url"], payload["sha256"], pkg, f)
total_download += len(data)
cabs += list(get_msi_cabs(data))
# download .cab files
for pkg in cabs:
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
with open(dst / pkg, "wb") as f:
download_progress(payload["url"], payload["sha256"], pkg, f)
print("Unpacking msi files...")
# run msi installers
for m in msi:
subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
### versions
msvcv = ""
sdkv = ""
if install_msvc:
msvcv = list((OUTPUT / "VC/Tools/MSVC").glob("*"))[0].name
if install_sdk:
sdkv = list((OUTPUT / "Windows Kits/10/bin").glob("*"))[0].name
# place debug CRT runtime into MSVC folder (not what real Visual Studio installer does... but is reasonable)
if install_msvc:
dst = str(OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{HOST}/{TARGET}")
pkg = "microsoft.visualcpp.runtimedebug.14"
dbg = packages[pkg][0]
payload = first(dbg["payloads"], lambda p: p["fileName"] == "cab1.cab")
try:
with tempfile.TemporaryFile(suffix=".cab", delete=False) as f:
data = download_progress(payload["url"], payload["sha256"], pkg, f)
total_download += len(data)
subprocess.check_call(["expand.exe", f.name, "-F:*", dst], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
finally:
os.unlink(f.name)
# place the folders under the Redist folder in the SDK under a versioned folder to allow other versions to be installed
if install_sdk:
redist_dir = OUTPUT / "Windows Kits/10/Redist"
redist_versioned_dir = redist_dir / f'{sdkv}'
if not os.path.exists(redist_versioned_dir):
os.makedirs(redist_versioned_dir)
for file_name in os.listdir(redist_dir):
if not file_name.startswith('10.0.'): # Simple heuristic
shutil.move((redist_dir / file_name), redist_versioned_dir)
### cleanup
shutil.rmtree(OUTPUT / "Common7", ignore_errors=True)
if install_msvc:
for f in ["Auxiliary", f"lib/{TARGET}/store", f"lib/{TARGET}/uwp"]:
shutil.rmtree(OUTPUT / "VC/Tools/MSVC" / msvcv / f)
for f in OUTPUT.glob("*.msi"):
f.unlink()
if install_sdk:
for f in ["Catalogs", "DesignTime", f"bin/{sdkv}/chpe", f"Lib/{sdkv}/ucrt_enclave"]:
shutil.rmtree(OUTPUT / "Windows Kits/10" / f, ignore_errors=True)
for arch in ["x86", "x64", "arm", "arm64"]:
if arch != TARGET:
if install_msvc:
shutil.rmtree(OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{arch}", ignore_errors=True)
if install_sdk:
shutil.rmtree(OUTPUT / "Windows Kits/10/bin" / sdkv / arch)
shutil.rmtree(OUTPUT / "Windows Kits/10/Lib" / sdkv / "ucrt" / arch)
shutil.rmtree(OUTPUT / "Windows Kits/10/Lib" / sdkv / "um" / arch)
### setup.bat
if install_msvc and install_sdk:
SETUP = f"""@echo off
set MSVC_VERSION={msvcv}
set MSVC_HOST=Host{HOST}
set MSVC_ARCH={TARGET}
set SDK_VERSION={sdkv}
set SDK_ARCH={TARGET}
set MSVC_ROOT=%~dp0VC\\Tools\\MSVC\\%MSVC_VERSION%
set SDK_INCLUDE=%~dp0Windows Kits\\10\\Include\\%SDK_VERSION%
set SDK_LIBS=%~dp0Windows Kits\\10\\Lib\\%SDK_VERSION%
set VCToolsInstallDir=%MSVC_ROOT%\\
set PATH=%MSVC_ROOT%\\bin\\%MSVC_HOST%\\%MSVC_ARCH%;%~dp0Windows Kits\\10\\bin\\%SDK_VERSION%\\%SDK_ARCH%;%~dp0Windows Kits\\10\\bin\\%SDK_VERSION%\\%SDK_ARCH%\\ucrt;%PATH%
set INCLUDE=%MSVC_ROOT%\\include;%SDK_INCLUDE%\\ucrt;%SDK_INCLUDE%\\shared;%SDK_INCLUDE%\\um;%SDK_INCLUDE%\\winrt;%SDK_INCLUDE%\\cppwinrt
set LIB=%MSVC_ROOT%\\lib\\%MSVC_ARCH%;%SDK_LIBS%\\ucrt\\%SDK_ARCH%;%SDK_LIBS%\\um\\%SDK_ARCH%
"""
(OUTPUT / "setup.bat").write_text(SETUP)
if install_msvc:
MSVC_SCRIPT = f"""@echo off
set MSVC_VERSION={msvcv}
set MSVC_HOST=Host{HOST}
set MSVC_ARCH={TARGET}
set MSVC_ROOT=%~dp0VC\\Tools\\MSVC\\%MSVC_VERSION%
set VCToolsInstallDir=%MSVC_ROOT%\\;%VCToolsInstallDir%
set PATH=%MSVC_ROOT%\\bin\\%MSVC_HOST%\\%MSVC_ARCH%;%PATH%
set INCLUDE=%MSVC_ROOT%\\include;%INCLUDE%
set LIB=%MSVC_ROOT%\\lib\\%MSVC_ARCH%;%LIB%
"""
(OUTPUT / f"msvc-{msvcv}.bat").write_text(MSVC_SCRIPT)
if install_sdk:
WIN10_SDK_SCRIPT = f"""@echo off
set SDK_VERSION={sdkv}
set SDK_ARCH={TARGET}
set SDK_INCLUDE=%~dp0Windows Kits\\10\\Include\\%SDK_VERSION%
set SDK_LIBS=%~dp0Windows Kits\\10\\Lib\\%SDK_VERSION%
set PATH=%~dp0Windows Kits\\10\\bin\\%SDK_VERSION%\\%SDK_ARCH%;%~dp0Windows Kits\\10\\bin\\%SDK_VERSION%\\%SDK_ARCH%\\ucrt;%PATH%
set INCLUDE=%SDK_INCLUDE%\\ucrt;%SDK_INCLUDE%\\shared;%SDK_INCLUDE%\\um;%SDK_INCLUDE%\\winrt;%SDK_INCLUDE%\\cppwinrt
set LIB=%SDK_LIBS%\\ucrt\\%SDK_ARCH%;%SDK_LIBS%\\um\\%SDK_ARCH%
"""
(OUTPUT / f"win-sdk-{sdkv}.bat").write_text(WIN10_SDK_SCRIPT)
print(f"Total downloaded: {total_download>>20} MB")
print("Done!")

279
win_setup.py Normal file
View File

@ -0,0 +1,279 @@
import devenver
import pprint
import subprocess
import sys
import pathlib
import os
import shutil
import tempfile
import devenver_manifest
import urllib.request
def git_clone(install_dir, git_exe, url, commit_hash):
devenver.lprint(f"Git clone {url} to {install_dir}", level=0)
# Clone repository if it does not exist
if not os.path.exists(install_dir):
devenver.lprint(f"Cloning to {install_dir}")
subprocess.run(f"{git_exe} clone {url} {install_dir}")
# Determine current git hash
result = subprocess.run(f"{git_exe} rev-parse --short HEAD",
cwd=install_dir,
capture_output=True)
curr_commit_hash = result.stdout.decode("utf-8").strip()
# Checkout correct target of Odin
if curr_commit_hash != commit_hash:
subprocess.run(f"{git_exe} checkout master", cwd=install_dir)
subprocess.run(f"{git_exe} pull origin master", cwd=install_dir)
subprocess.run(f"{git_exe} checkout {commit_hash}", cwd=install_dir)
# Run DEVenver, installing the portable apps
# ------------------------------------------------------------------------------
user_app_list = devenver_manifest.get_manifest()
installed_apps = devenver.run(user_app_list)
# Install MSVC
# ------------------------------------------------------------------------------
devenver.print_header("Install MSVC & Windows 10 SDK")
msvc_script = pathlib.Path(devenver.script_dir, "win_portable_msvc.py")
msvc_version = "14.34"
win10_sdk_version = "22621"
msvc_install_dir = devenver.base_install_dir / "msvc"
# Basic heuristic to see if we"ve already installed the MSVC/SDK version
msvc_installed = False
win10_sdk_installed = False
msvc_find_test_dir = msvc_install_dir / "VC/Tools/MSVC"
win10_sdk_find_test_dir = msvc_install_dir / "Windows Kits/10"
if os.path.exists(msvc_find_test_dir):
for file_name in os.listdir(msvc_find_test_dir):
msvc_installed = file_name.startswith(msvc_version)
if msvc_installed == True:
devenver.lprint(f"MSVC {msvc_version} install detected (skip download) in {msvc_find_test_dir}\\{file_name}")
break
if not msvc_installed:
devenver.lprint(f"MSVC {msvc_version} install not detected (need to download) in {msvc_find_test_dir}")
if os.path.exists(win10_sdk_find_test_dir):
for file_name in os.listdir(win10_sdk_find_test_dir / "bin"):
# Check if directory contains version substring, 22621, e.g. "10.0.22621.0"
win10_sdk_installed = file_name.count(win10_sdk_version) > 0
if win10_sdk_installed == True:
install_locations = f"{win10_sdk_find_test_dir}\\*\\{file_name}"
devenver.lprint(f"Windows 10 SDK {win10_sdk_version} install detected (skip download) in {install_locations}")
break
if not win10_sdk_installed:
devenver.lprint(f"Windows 10 SDK {win10_sdk_version} not detected (need to download) in {win10_sdk_find_test_dir}")
# Install MSVC
if msvc_installed == False or win10_sdk_installed == False:
with tempfile.TemporaryDirectory() as temp_dir:
# Invoke the MSVC script to download MSVC to disk
command = f"'{sys.executable}' '{msvc_script}' --accept-license"
line = "Invoking MSVC script to install"
if msvc_installed:
command += " --no-msvc"
else:
command += f" --msvc-version {msvc_version}"
line += f" MSVC {msvc_version}"
if win10_sdk_installed:
command += " --no-sdk"
else:
command += f" --sdk-version {win10_sdk_version}"
line += f" Windows 10 SDK {win10_sdk_version}"
devenver.lprint(line)
devenver.lprint(f"Command: {command}")
subprocess.run(command, cwd=temp_dir)
# Merge the download MSVC installation to our unified install dir
temp_msvc_dir = pathlib.Path(temp_dir, "msvc")
for src_dir, dirs, files in os.walk(temp_msvc_dir):
install_dir = src_dir.replace(str(temp_msvc_dir), str(msvc_install_dir), 1)
if not os.path.exists(install_dir):
os.makedirs(install_dir)
for file_ in files:
src = os.path.join(src_dir, file_)
dest = os.path.join(install_dir, file_)
if os.path.exists(dest):
if os.path.samefile(src, dest):
continue
os.remove(dest)
shutil.move(src, install_dir)
devenver.lprint(f"MSVC {msvc_version} Windows 10 SDK {win10_sdk_version} installed: {msvc_install_dir}")
# Install apps dependent on Git
# ------------------------------------------------------------------------------
devenver.print_header("Install apps that rely on Git")
git_exe = installed_apps["Git"][0]['exe_path']
# Clink Completions
# ------------------------------------------------------------------------------
clink_git_hash = "fa18736"
clink_install_dir = pathlib.Path(devenver.base_install_dir, "clink-completions")
git_clone(install_dir=clink_install_dir,
git_exe=git_exe,
url="https://github.com/vladimir-kotikov/clink-completions",
commit_hash=clink_git_hash)
# Odin
# ------------------------------------------------------------------------------
odin_git_hash = "9ae1bfb6"
odin_install_dir = pathlib.Path(devenver.base_install_dir, "Odin")
git_clone(install_dir=odin_install_dir,
git_exe=git_exe,
url="https://github.com/odin-lang/odin.git",
commit_hash=odin_git_hash)
# TODO: We can't do this yet because the odin build requires a registry hack so
# that it knows where to find MSVC.
# Build Odin
# subprocess.run(f"{git_exe} checkout {odin_git_hash}",
# cwd=odin_install_dir)
# Install left-overs
# ------------------------------------------------------------------------------
devenver.print_header("Install configuration files")
# Copy init.vim to NVIM directory
internal_dir = pathlib.Path(os.path.dirname(os.path.abspath(__file__)), "Internal")
nvim_init_dir = pathlib.Path(os.path.expanduser("~"), "AppData", "Local", "nvim")
nvim_config_dest_path = nvim_init_dir / "init.vim"
nvim_config_src_path = internal_dir / "os_nvim_init.vim"
devenver.lprint(f"Installing NVIM config to {nvim_config_dest_path}")
nvim_init_dir.mkdir(parents=True, exist_ok=True)
shutil.copy(nvim_config_src_path, nvim_config_dest_path)
# Download vim.plug to NVIM init directory
nvim_plug_vim_dir = nvim_init_dir / "autoload"
nvim_plug_vim_path = nvim_plug_vim_dir / "plug.vim"
nvim_plug_vim_dir.mkdir(parents=True, exist_ok=True)
if not os.path.exists(nvim_plug_vim_path):
devenver.lprint(f"Installing NVIM plugin manager to {nvim_plug_vim_path}")
urllib.request.urlretrieve("https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim",
nvim_plug_vim_path)
# Install wezterm configuration
wezterm_install_dir = installed_apps["WezTerm"][0]["install_dir"]
wezterm_exe_path = installed_apps["WezTerm"][0]["exe_path"]
wezterm_config_dest_path = wezterm_install_dir / "wezterm.lua"
devenver.lprint(f"Installing WezTerm config to {wezterm_config_dest_path}")
clink_install_dir = installed_apps["clink"][0]["install_dir"]
clink_exe_path = clink_install_dir.relative_to(devenver.base_install_dir) / "clink_x64.exe"
clink_exe_path_for_wezterm = str(clink_exe_path).replace("\\", "\\\\")
wezterm_lua_buffer = f"""local wezterm = require 'wezterm';
local default_prog
local set_environment_variables = {{}}
if wezterm.target_triple == "x86_64-pc-windows-msvc" then
clink_exe = string.format("%s\\\\..\\\\..\\\\{clink_exe_path_for_wezterm}", wezterm.executable_dir)
devenv_bat = string.format("%s\\\\..\\\\..\\\\devenv.bat", wezterm.executable_dir)
msvc_bat = string.format("%s\\\\..\\\\..\\\\msvc\\\\setup.bat", wezterm.executable_dir)
-- Taken from: https://wezfurlong.org/wezterm/shell-integration.html
-- Use OSC 7 as per the above example
set_environment_variables['prompt'] =
'$E]7;file://localhost/$P$E\\\\$E[32m$T$E[0m $E[35m$P$E[36m$_$G$E[0m '
-- use a more ls-like output format for dir
set_environment_variables['DIRCMD'] = '/d'
default_prog = {{"cmd.exe", "/s", "/k",
clink_exe, "inject", "-q",
"&&", "call", devenv_bat,
"&&", "call", msvc_bat}}
end
return {{
font_size = 10.0,
color_scheme = "Peppermint",
default_prog = default_prog,
set_environment_variables = set_environment_variables,
}}
"""
with open(wezterm_config_dest_path, "w") as file:
file.write(wezterm_lua_buffer)
# Wezterm super terminal
wezterm_exe_rel_path = pathlib.Path(wezterm_exe_path).relative_to(devenver.base_install_dir)
wezterm_terminal_script_path = pathlib.Path(devenver.base_install_dir, "win_terminal.bat")
wezterm_terminal_script = f"""@echo off
setlocal EnableDelayedExpansion
set working_dir=
if "%~1" neq "" (
set working_dir=start --cwd "%~1"
set working_dir=!working_dir:\=/!
)
if exist "%~dp0win_terminal_user_config.bat" call "%~dp0win_terminal_user_config.bat"
start "" /MAX "%~dp0{wezterm_exe_rel_path}" !working_dir!
"""
devenver.lprint(f"Installing WezTerm terminal script to {wezterm_terminal_script_path}")
with open(wezterm_terminal_script_path, "w") as file:
file.write(wezterm_terminal_script)
# Create Odin work-around scripts
# Odin uses J. Blow's Microsoft craziness SDK locator which relies on the
# registry. Here we inject the registry entry that the SDK locator checks for
# finding our portable MSVC installation.
win10_sdk_find_test_dir_reg_path = str(win10_sdk_find_test_dir).replace("\\", "\\\\")
odin_msvc_install_script = f"""Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots]
"KitsRoot10"="{win10_sdk_find_test_dir_reg_path}"
"""
odin_msvc_uninstall_script = f"""Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots]
"KitsRoot10"=-
"""
odin_msvc_install_script_path = devenver.base_install_dir / "odin_msvc_install_workaround.reg"
odin_msvc_uninstall_script_path = devenver.base_install_dir / "odin_msvc_uninstall_workaround.reg"
devenver.lprint(f"Installing Odin MSVC workaround scripts", level=0)
devenver.lprint(f" - {odin_msvc_install_script_path}", level=1)
devenver.lprint(f" - {odin_msvc_uninstall_script_path}", level=1)
with open(odin_msvc_install_script_path, "w") as file:
file.write(odin_msvc_install_script)
with open(odin_msvc_uninstall_script_path, "w") as file:
file.write(odin_msvc_uninstall_script)
# Add python-update bootstrap script
# TODO: If I'm using the terminal that this script generates it will lock the
# executable and Python cannot open the file for verifying the SHA256.
python_exe = pathlib.Path(installed_apps["Python"][0]['exe_path']).relative_to(devenver.base_install_dir)
python_install_dir = pathlib.Path(installed_apps["Python"][0]['exe_path']).parent.relative_to(devenver.base_install_dir)
win_setup_script_path = pathlib.Path(devenver.script_dir, "win_setup.py")
manifest_script_path = pathlib.Path(devenver.script_dir, "devenver_manifest.py")
bootstrap_setup_script = f"""@echo off
setlocal EnableDelayedExpansion
set PYTHONHOME=%~dp0{python_install_dir}
%~dp0{python_exe} {win_setup_script_path} --manifest-file {manifest_script_path}
pause
"""
with open(devenver.base_install_dir / "upgrade_bootstrap.bat", "w") as file:
file.write(bootstrap_setup_script)