Skip to content

Instantly share code, notes, and snippets.

@joshcangit
Last active January 22, 2023 16:30
Show Gist options
  • Save joshcangit/2e2f4ad62ba3122c4ab2e6adedbb1b44 to your computer and use it in GitHub Desktop.
Save joshcangit/2e2f4ad62ba3122c4ab2e6adedbb1b44 to your computer and use it in GitHub Desktop.
Wrapper script for meson.pyz
#!/usr/bin/env python3
from asyncio import run as arun
from functools import partial as fpartial
from importlib import util as libutil
from pathlib import Path
from re import sub
from shutil import move, which
from subprocess import run, PIPE, STDOUT
from sys import argv, executable, stdout
from tarfile import open as topen
from tempfile import TemporaryDirectory
from zipapp import create_archive
try: from packaging.version import Version
except ModuleNotFoundError:
from pkg_resources import packaging
Version = packaging.version.Version
del packaging
cmds = ["aria2c","xh","curl","wget"]
cmds[:] = [x for x in cmds if which(x)]
libr = "requests"
if(libutil.find_spec(libr)):
from requests import get as getr, head as headr
cmds += [libr]
def if_exists(fp):
if(Path(fp).exists()):
return fp
else: return None
def cmd_output(cmd):
result = run(cmd, shell=True, text=True, check=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
return result.stdout
def tag_version(url):
return sub(r'.+\/(.+)', '\\1', url)
def file_url(url):
return sub(r'(.+)releases\/tag(.+)', '\\1archive/refs/tags\\2.tar.gz', url)
async def location(cmds, url):
cmds = [x for x in cmds if x != "aria2c"]
xh_v = cmd_output('xh --version').split() if "xh" in cmds else ["xh","1"]
cmd_dict = {
"xh": f"xh HEAD {url} User-Agent:'{xh_v[0]}:{xh_v[1]}' --no-check-status -h",
"curl": f"curl -ISs {url}",
"wget": f"wget --recursive=off --method=HEAD --max-redirect=1 {url}",
libr: None
}
cmd = cmd_dict[cmds[0]]
if (cmd):
return [x for x in cmd_output(cmd).splitlines() if x.startswith("location") or x.startswith("Location")][0].split()[1]
else:
response = headr(url)
return response.headers["Location"]
async def write_loop(r,f,length=None):
part = 0
for chunk in r.iter_content(chunk_size=1024):
f.write(chunk)
if (length):
part += len(chunk)
percent = int(part / length * 100) if (length) else 0
stdout.write(f"\rProgress: {percent}%")
stdout.flush()
stdout.write('\rDownload completed.\n')
async def file_writer(r,file,file_size=None):
with open(file, 'wb') as f:
await write_loop(r,f,int(file_size)) if (file_size) else await write_loop(r,f)
async def get_request(url,file,headers=None):
with getr(url, headers=headers, stream=True) as r:
print(f"Downloading to {file}")
if (headers): r.raw.read = fpartial(r.raw.read, decode_content=True)
file_size = r.headers.get('content-length')
await file_writer(r,file,file_size) if (file_size) else await file_writer(r,file)
async def download_file(url, file=None, encoding=None):
if (file is None): file = url.split('/')[-1]
headers = {'Accept-Encoding': encoding} if (encoding) else None
await get_request(url,file,headers)
async def install(cmds, url, ver, exe):
file = "meson.tgz"
options = {
"source": f"meson-{ver}",
"outfile": exe,
"interpreter": "/usr/bin/env python3",
"compress": True
}
encoding = "zstd, gzip, deflate"
with TemporaryDirectory() as d:
cmd_dict = {
"aria2c": f"aria2c {url} -d '{d}' -o '{file}' -j5 -x10 -s8 -k1M --optimize-concurrent-downloads --header='Accept-Encoding: {encoding}'",
"xh": f"xh {url} Accept-Encoding:'{encoding}' -Fdo '{Path(d) / file}'",
"curl": f"curl -L {url} -o '{Path(d) / file}' --compressed -H 'Accept-Encoding: {encoding}'",
"wget": f"wget {url} -P '{d}' -O '{file}' --header='Accept-Encoding: {encoding}'",
libr: None
}
cmd = cmd_dict[cmds[0]]
file = Path(d) / file
run(cmd, shell=True, check=True) if (cmd) else await download_file(url,file,encoding)
with topen(file) as tar:
maindir = options["source"]
group = [
tarinfo for tarinfo in tar.getmembers()
if tarinfo.name.startswith(f"{maindir}/mesonbuild/") or tarinfo.name == f"{maindir}/meson.py"
]
tar.extractall(d,members=group)
Path(file).unlink()
source = Path(d, options["source"]).resolve()
move(source / 'meson.py', Path(d, '__main__.py'))
move(source / 'mesonbuild', Path(d, 'mesonbuild'))
Path(source).rmdir()
create_archive(d, interpreter=options["interpreter"], target=options["outfile"], compressed=options["compress"])
if (if_exists(exe)):
try: move(exe,dirpath / exe)
except IOError:
admin_cmd = f"from pathlib import Path\nfrom shutil import copymode, move, chown\nmove('{exe}','{dirpath / exe}')\nchown('{dirpath / exe}',user='{Path(dirpath).owner()}',group='{Path(dirpath).group()}')\ncopymode('{__file__}','{dirpath / exe}')"
run(f'sudo python -c "{admin_cmd}"', shell=True, check=True)
meson = "meson.pyz"
release = "https://github.com/mesonbuild/meson/releases/latest"
dirpath = Path(__file__).parent.absolute()
subcmd = "pyz"
if (if_exists(dirpath / meson)):
if (argv == [argv[0], subcmd]):
tag_url = arun(location(cmds, release))
latest_version = tag_version(tag_url)
current_version = cmd_output(f"{executable} {dirpath / meson} --version").strip()
if (Version(current_version) < Version(latest_version)):
arun(install(cmds, file_url(tag_url), latest_version, meson))
else: print("Meson is up to date")
else:
argv[:] = [f"\"{x}\"" if " " in x else x for x in argv]
run([executable, f"{dirpath / meson}"] + argv[1:])
else:
if (argv == [argv[0], subcmd]):
tag_url = arun(location(cmds, release))
latest_version = tag_version(tag_url)
arun(install(cmds, file_url(tag_url), latest_version, meson))
else: print(f"\nCommand 'meson' not found, but can be installed with:\n\nmeson {subcmd}\n")
@joshcangit
Copy link
Author

joshcangit commented May 12, 2022

Wrapper script for meson.pyz

Registering as command

On Linux / BSD / macOS

PATH environment

  • Place this script inside any folder in your PATH environment.

e.g. /usr/local/bin or ~/.local/bin

  • Instead if you want it in a different folder, create a folder.
    Then, add the contents as below to a profile file, corresponding to on the folder you made, to append to your PATH environment.
    e.g. ~/.meson/bin
if [ -d "$HOME/.meson/bin" ] ; then
    export PATH="$PATH:$HOME/.meson/bin"
fi

The per-user profile file is .profile.
Shell specific files are either .bash-profile for Bash and .zsh_profile for Z shell.
System level profile files are either /etc/profile or a .sh file in /etc/profile.d

Rename file

  • Rename this file to remove the file extension.

i.e. Use meson as filename.

On Windows

Path environment

Place this script inside any folder in your Path environment.

e.g. %SYSTEMROOT%\System32\WindowsPowerShell\v1.0

  • Instead if you want it in a different folder, create a folder.
    Then, open sysdm.cpl a.k.a System Properties.

Windows 10/8.1/7:
Control Panel -> System and Security -> System -> Advanced system settings
Windows 11:
Settings -> System -> About -> Related links section -> Advanced system settings

  • After that, go to the Environment Variables dialog to add the folder path either with the New or Browse... button.

In System Properties -> Advanced tab -> Environment Variables... button
Either Path in the User variables or System variables section.

Add .cmd script.

Create a meson.cmd script, in the same folder, with the contents as below

@echo off
meson.py %*

Do not use this with another Meson instance installed.

This is if you prefer to use Meson directly from the GitHub releases through this script.

Dependencies

Python3

This script requires Python 3.6 and above because I have use formatted string literals.

I prefer to do use this over % or format().

Anyway, Meson itself requires at least Python 3.7.

Also, Python 2 is already deprecated so it should be safe to assume it won't be used.

Download functionality

For this script to be able to download, any one of 4 tools below is required.

xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance.

Command line tool and library for transferring data with URLs. (since 1998)

GNU Wget is a free software package for retrieving files using HTTP, HTTPS, FTP and FTPS, the most widely used Internet protocols.

  • requests
    There are a few ways to install this.
    This can be installed using pip install requests.
    This can also be installed as Linux packages named python3-requests or as with Arch python-requests.

Easier to implement a progress indicator with requests than the built-in http.client library.

sudo

On Windows, you can install gsudo if just in case when this script uses sudo for admin rights to a folder.

Optional dependencies

aria2

There's another tool called aria2 but it cannot send output for this script to fetch the download URL.
It can only be used for downloading, albeit very fast with it's multi-connection download capability.
Any of the other tools is still needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment