From 58b19699b9bcae8ffcee572ab93324ab172a5d9f Mon Sep 17 00:00:00 2001 From: Daylin Morgan Date: Mon, 19 Sep 2022 02:16:24 -0500 Subject: [PATCH] update .task.mk --- .task.mk | 330 +++++++++++++++++++++++++++++-------------------------- Makefile | 10 +- 2 files changed, 177 insertions(+), 163 deletions(-) diff --git a/.task.mk b/.task.mk index 1c4af60..ca6b2c4 100644 --- a/.task.mk +++ b/.task.mk @@ -1,155 +1,167 @@ # }> [github.com/daylinmorgan/task.mk] <{ # # Copyright (c) 2022 Daylin Morgan # MIT License -# 22.9.5 +# version: 22.9.19 # -# task.mk should be included at the bottom of your Makefile. +# task.mk should be included at the bottom of your Makefile with `-include .task.mk` # See below for the standard configuration options that should be set prior to including this file. # You can update your .task.mk with `make _update-task.mk` - -# ---- CONFIG ---- # -HEADER_COLOR ?= b_cyan -PARAMS_COLOR ?= b_magenta -ACCENT_COLOR ?= b_yellow -GOAL_COLOR ?= $(ACCENT_COLOR) -MSG_COLOR ?= faint -HELP_SEP ?= | -HELP_SORT ?= # sort goals alphabetically - +# ---- [config] ---- # +HEADER_STYLE ?= b_cyan +ACCENT_STYLE ?= b_yellow +PARAMS_STYLE ?= $(ACCENT_STYLE) +GOAL_STYLE ?= $(ACCENT_STYLE) +MSG_STYLE ?= faint +DIVIDER_STYLE ?= default +DIVIDER ?= ─ +HELP_SEP ?= │ # python f-string literals EPILOG ?= -define USAGE ?= -{ansi.$(HEADER_COLOR)}usage{ansi.end}: - make - -endef - +USAGE ?={ansi.$(HEADER_STYLE)}usage{ansi.end}:\n make \n # ---- [buitlin recipes] ---- # - - ## h, help | show this help .PHONY: help h help h: $(call py,help_py) - .PHONY: _help _help: export SHOW_HIDDEN=true _help: help - ifdef PRINT_VARS - $(foreach v,$(PRINT_VARS),$(eval export $(v))) - .PHONY: vars v vars v: $(call py,vars_py,$(PRINT_VARS)) - endif - +### | args: -ws --hidden +### task.mk builtins: | args: -d --hidden ## _print-ansi | show all possible ansi color code combinations .PHONY: _print-ansi: $(call py,print_ansi_py) - # functions to take f-string literals and pass to python print tprint = $(call py,info_py,$(1)) tprint-sh = $(call pysh,info_py,$(1)) - +tconfirm = $(call py,confirm_py,$(1)) +## _update-task.mk | downloads latest development version of task.mk _update-task.mk: - $(call tprint,Updating task.mk) + $(call tprint,{a.b_cyan}Updating task.mk{a.end}) curl https://raw.githubusercontent.com/daylinmorgan/task.mk/main/task.mk -o .task.mk - export MAKEFILE_LIST - # ---- [python/bash script runner] ---- # - -# modified from https://unix.stackexchange.com/a/223093 define \n endef - escape_shellstring = $(subst `,\`,$(subst ",\",$(subst $$,\$$,$(subst \,\\,$1)))) - escape_printf = $(subst \,\\,$(subst %,%%,$1)) - create_string = $(subst $(\n),\n,$(call escape_shellstring,$(call escape_printf,$1))) - - +printline = printf -- "<----------------------------------->\n" ifdef DEBUG -define py -@printf "Python Script:\n" -@printf -- "----------------\n" -@printf "$(call create_string,$($(1)))\n" -@printf -- "----------------\n" -@printf "$(call create_string,$($(1)))" | python3 -endef -define tbash -@printf "Bash Script:\n" -@printf -- "----------------\n" -@printf "$(call create_string,$($(1)))\n" -@printf -- "----------------\n" -@printf "$(call create_string,$($(1)))" | bash +define _debug_runner +@printf "$(1) Script:\n";$(printline); +@printf "$(call create_string,$(3))\n" | cat -n +@$(printline) +@printf "$(call create_string,$(3))" | $(2) endef +py = $(call _debug_runner,Python,python3,$($(1))) +tbash = $(call _debug_runner,Bash,bash,$($(1))) else -py = @printf "$(call create_string,$($(1)))" | python3 -tbash = @printf "$(call create_string,$($(1)))" | bash +py = @python3 <(printf "$(call create_string,$($(1)))") +tbash = @bash <(printf "$(call create_string,$($(1)))") endif - -pysh = printf "$(call create_string,$($(1)))" | python3 - +pysh = python3 <(printf "$(call create_string,$($(1)))") # ---- [python scripts] ---- # - - define help_py - - +import argparse +from collections import namedtuple import os import re - $(ansi_py) - -pattern = re.compile(r"^## (.*) \| (.*)") - -makefile = "" -for file in os.getenv("MAKEFILE_LIST").split(): - with open(file, "r") as f: - makefile += f.read() + "\n\n" - - -def get_help(file): +MaxLens = namedtuple("MaxLens", "goal msg") +pattern = re.compile( + r"^## (?P.*?) \| (?P.*?)(?:\s?\| args: (?P.*?))?$$|^### (?P.*?)?(?:\s?\| args: (?P.*?))?$$" +) +def parseargs(argstring): + parser = argparse.ArgumentParser() + parser.add_argument("--align") + parser.add_argument("-d", "--divider", action="store_true") + parser.add_argument("-ws", "--whitespace", action="store_true") + parser.add_argument("-ms", "--msg-style", type=str) + parser.add_argument("-gs", "--goal-style", type=str) + parser.add_argument("--hidden", action="store_true") + return parser.parse_args(argstring.split()) +def gen_makefile(): + makefile = "" + for file in os.getenv("MAKEFILE_LIST").split(): + with open(file, "r") as f: + makefile += f.read() + "\n\n" + return makefile +def parse_make(file): for line in file.splitlines(): match = pattern.search(line) if match: - if not os.getenv("SHOW_HIDDEN") and match.groups()[0].startswith("_"): - continue + if not os.getenv("SHOW_HIDDEN") and str( + match.groupdict().get("goal") + ).startswith("_"): + pass else: - yield match.groups() - - -print(f"""$(USAGE)""") - -goals = list(get_help(makefile)) -if os.getenv("SORT_HELP",False): - goals.sort(key=lambda i: i[0]) -goal_len = max(len(goal[0]) for goal in goals) - -for goal, msg in goals: - print( - f"{ansi.$(GOAL_COLOR)}{goal:>{goal_len}}{ansi.end} $(HELP_SEP) {ansi.$(MSG_COLOR)}{msg}{ansi.end}" + yield {k: v for k, v in match.groupdict().items() if v is not None} +def fmt_goal(goal, msg, max_goal_len, argstr): + args = parseargs(argstr) + goal_style = args.goal_style.strip() if args.goal_style else "$(GOAL_STYLE)" + msg_style = args.msg_style.strip() if args.msg_style else "$(MSG_STYLE)" + return ( + ansi.style(f" {goal:>{max_goal_len}}", goal_style) + + f" $(HELP_SEP) " + + ansi.style(msg, msg_style) ) - -print(f"""$(EPILOG)""") - - +def fmt_rawmsg(msg, argstr, maxlens): + args = parseargs(argstr) + lines = [] + msg_style = args.msg_style.strip() if args.msg_style else "$(MSG_STYLE)" + if not os.getenv("SHOW_HIDDEN") and args.hidden: + return [] + if msg: + if args.align == "sep": + lines.append( + f"{' '*(maxlens.goal+len('$(HELP_SEP)')+4)}{ansi.style(msg,msg_style)}" + ) + elif args.align == "center": + lines.append(f" {ansi.style(msg.center(sum(maxlens)),msg_style)}") + else: + lines.append(f" {ansi.style(msg,msg_style)}") + if args.divider: + lines.append( + ansi.style( + f" {'$(DIVIDER)'*(len('$(HELP_SEP)')+sum(maxlens)+2)}", + "$(DIVIDER_STYLE)", + ) + ) + if args.whitespace: + lines.append("\n") + return lines +def print_help(): + lines = [f"""$(USAGE)"""] + items = list(parse_make(gen_makefile())) + maxlens = MaxLens( + *(max((len(item[x]) for item in items if x in item)) for x in ["goal", "msg"]) + ) + for item in items: + if "goal" in item: + lines.append( + fmt_goal( + item["goal"], item["msg"], maxlens.goal, item.get("msgargs", "") + ) + ) + if "rawmsg" in item: + lines.extend(fmt_rawmsg(item["rawmsg"], item.get("rawargs", ""), maxlens)) + lines.append(f"""$(EPILOG)""") + print("\n".join(lines)) +print_help() endef - define ansi_py - - import os import sys - color2byte = dict( black=0, red=1, @@ -160,91 +172,101 @@ color2byte = dict( cyan=6, white=7, ) - state2byte = dict( bold=1, faint=2, italic=3, underline=4, blink=5, fast_blink=6, crossed=9 ) - - -def fg(byte): - return 30 + byte - - -def bg(byte): - return 40 + byte - - +addfg = lambda byte: byte + 30 +addbg = lambda byte: byte + 40 class Ansi: - """ANSI color codes""" - + """ANSI escape codes""" + def __init__(self): + self.setcode("end", "\033[0m") + self.setcode("default", "\033[38m") + self.setcode("bg_default", "\033[48m") + for name, byte in color2byte.items(): + self.setcode(name, f"\033[{addfg(byte)}m") + self.setcode(f"b_{name}", f"\033[1;{addfg(byte)}m") + self.setcode(f"d_{name}", f"\033[2;{addfg(byte)}m") + for bgname, bgbyte in color2byte.items(): + self.setcode( + f"{name}_on_{bgname}", f"\033[{addbg(bgbyte)};{addfg(byte)}m" + ) + for name, byte in state2byte.items(): + self.setcode(name, f"\033[{byte}m") def setcode(self, name, escape_code): + """create attr for style and escape code""" if not sys.stdout.isatty() or os.getenv("NO_COLOR", False): setattr(self, name, "") else: setattr(self, name, escape_code) - - def __init__(self): - self.setcode("end", "\033[0m") - for name, byte in color2byte.items(): - self.setcode(name, f"\033[{fg(byte)}m") - self.setcode(f"b_{name}", f"\033[1;{fg(byte)}m") - self.setcode(f"d_{name}", f"\033[2;{fg(byte)}m") - for bgname, bgbyte in color2byte.items(): - self.setcode(f"{name}_on_{bgname}", f"\033[{bg(bgbyte)};{fg(byte)}m") - for name, byte in state2byte.items(): - self.setcode(name, f"\033[{byte}m") - - + def custom(self, fg=None, bg=None): + """use custom color""" + code, end = "\033[", "m" + if fg: + if isinstance(fg, int): + code += f"38;5;{fg}" + elif (isinstance(fg, list) or isinstance(fg, tuple)) and len(fg) == 1: + code += f"38;5;{fg[0]}" + elif (isinstance(fg, list) or isinstance(fg, tuple)) and len(fg) == 3: + code += f"38;2;{';'.join((str(i) for i in fg))}" + else: + print("Expected one or three values for fg as a list") + sys.exit(1) + if bg: + if isinstance(bg, int): + code += f"{';' if fg else ''}48;5;{bg}" + elif (isinstance(bg, list) or isinstance(bg, tuple)) and len(bg) == 1: + code += f"{';' if fg else ''}48;5;{bg[0]}" + elif (isinstance(bg, list) or isinstance(bg, tuple)) and len(bg) == 3: + code += f"{';' if fg else ''}48;2;{';'.join((str(i) for i in bg))}" + else: + print("Expected one or three values for bg as a list") + sys.exit(1) + return code + end + def style(self, text, style): + if style not in self.__dict__: + print(f"unknown style: {style}") + sys.exit(1) + else: + return f"{self.__dict__[style]}{text}{self.__dict__['end']}" a = ansi = Ansi() - - endef - define info_py - - $(ansi_py) - print(f"""$(2)""") - - endef - define print_ansi_py - - $(ansi_py) - -codes_names = { - getattr(ansi, attr): attr - for attr in dir(ansi) - if attr[0:1] != "_" and attr != "end" and attr != "setcode" -} +sep = f"$(HELP_SEP)" +codes_names = {getattr(ansi, attr): attr for attr in ansi.__dict__} for code in sorted(codes_names.keys(), key=lambda item: (len(item), item)): - print("{:>20} {}".format(codes_names[code], code + "******" + ansi.end)) - - - + print(f"{codes_names[code]:>20} {sep} {code+'*****'+ansi.end} {sep} {repr(code)}") endef - define vars_py - - - import os - $(ansi_py) - vars = "$2".split() length = max((len(v) for v in vars)) - -print(f"{ansi.$(HEADER_COLOR)}vars:{ansi.end}\n") - +print(f"{ansi.$(HEADER_STYLE)}vars:{ansi.end}\n") for v in vars: print(f" {ansi.b_magenta}{v:<{length}}{ansi.end} = {os.getenv(v)}") - print() - - endef - +define confirm_py +import sys +$(ansi_py) +def confirm(): + """ + Ask user to enter Y or N (case-insensitive). + :return: True if the answer is Y. + :rtype: bool + """ + answer = "" + while answer not in ["y", "n"]: + answer = input(f"""$(2) {a.b_red}[Y/n]{a.end} """).lower() + return answer == "y" +if confirm(): + sys.exit(0) +else: + sys.exit(1) +endef diff --git a/Makefile b/Makefile index d53e699..462e145 100644 --- a/Makefile +++ b/Makefile @@ -45,13 +45,5 @@ lint: clean: @rm -r patched/* -define USAGE -{a.b_green}Update MonoLisa with Nerd Fonts! {a.end} - -{a.$(HEADER_COLOR)}usage{a.end}: - make - -endef - +USAGE = {a.b_green}Update MonoLisa with Nerd Fonts! {a.end}\n\n{a.$(HEADER_STYLE)}usage{a.end}:\n make \n -include .task.mk -$(if $(filter help,$(MAKECMDGOALS)),.task.mk: ; curl -fsSL https://raw.githubusercontent.com/daylinmorgan/task.mk/v22.9.5/task.mk -o .task.mk)