## Friday, December 11, 2020

### Making high-resolution screenshots of Emacs frames

Emacs 27.1 can utilise Cairo drawing backend to take screenshots of itself via x-export-frames function. Unfortunately, the bare bone function is all we have here–there's no UI to it. Moreover, it doesn't support bitmap fonts, which means if you still use, say, Terminus, you get garbage in the output.

I wanted to share a screenshot of a Emacs frame on twitter. Twitter doesn't accept SVGs, for net income of $1.47bn isn't enough to support such a complex thing. The best way to obtain an arbitrary high-resolution png is to get it from a vector image. I found that postscript->png gives the best results & requires only ghostscript installed. (defun my--screenshot-png(out) "Save a screenshot of the current frame as a png file. Requires ghostscript." (let ((ps (concat out ".tmp"))) (my--screenshot ps 'postscript) (call-process "gs" nil (get-buffer-create "*Shell Command Output*") nil "-sDEVICE=png16m" "-dBATCH" "-dNOPAUSE" "-r300" "-dTextAlphaBits=4" "-dGraphicsAlphaBits=4" (concat "-sOutputFile=" out) ps) (delete-file ps) )) We use 300 dpi here to render a png. my--screenshot function below temporally changes a frame font to Inconsolata: (defun my--screenshot(out format) (let ((fontdef (face-attribute 'default :font))) (set-frame-font "Inconsolata 10") (unwind-protect (with-temp-file out (insert (x-export-frames nil format))) (set-frame-font fontdef)) )) The last bit left is to provide a prompt for a user where to save the screenshot: (defun my-ss() "Save a screenshot of the current frame in a file" (interactive) (let* ((out (expand-file-name (read-file-name "Output file name: "))) (ext (file-name-extension out))) (cond ((equal "png" ext) (my--screenshot-png out)) ((equal "ps" ext) (my--screenshot out 'postscript)) (t (my--screenshot out (intern ext))) ))) E.g.: M-x my-ss<RET>Output file name: ~/Downloads/1.png<RET> The physical image size here is 3133x3642. ### How I read newsgroups in mutt It took me a long time, but I've finally removed inn+newsstar from my machine. I don't use patches that add NNTP support to mutt any more. Yet, I still cannot put gmane away, for it's much more convenient to read mailing lists as newsgroups. What if I just fetch last N posts from newsgroup foo.a & save them in an mbox file for viewing it mutt later on? Then I can do the same for newsgroups foo.b, foo.c, & so on. How do I fetch? Turns out, there is a nice CLI NNTP client already, called sinntp. The following command downloads fresh articles from comp.lang.c into a conspicuously named mbox file comp.lang.c: $ sinntp pull --server news.eternal-september.org comp.lang.c

If you run it again, it won't re-download the same articles again, for it saves reported high water mark in ~/.local/share/sinntp/comp.lang.c@news.eternal-september.org file.

This only solves a problem for 1 news server & 1 newsgroup. I read multitudes of them; should I write a simple shell script then? If you follow this blog, you may have noticed I try not to employ shell scripts but write makefiles instead.

$cat ~/.config/nntp2mbox/news.eternal-september.org.confcomp.lang.c This states that I want to grab articles from comp.lang.c newsgroup from news.eternal-september.org news server. $ cat ~/.config/nntp2mbox/news.gmane.io.confgmane.comp.gnu.make.develgmane.comp.gnu.make.general#gmane.comp.web.chromium.extensionsgmane.comp.window-managers.fvwm

In this example, the server name is news.gmane.io & 1 of the newsgroups is commented out.

There's no more configuration, everything else is done by nntp2mbox makefile:

#!/usr/bin/make -f# a number of article to pulllimit := 500g :=conf := $(or$(XDG_CONFIG_HOME),~/.config)/nntp2mboxservers := $(wildcard$(conf)/*.conf)self := $(lastword$(MAKEFILE_LIST))all: $(servers:%.conf=%.server)# read a list of newsgroups & run Make for each newsgroup%.server: %.conf awk '!/^#/ {print $$1 ".newsgroup"}' < | grep (call se,(g)) | xargs -r (make) -Bk server=(notdir (basename <))%.newsgroup: sinntp pull --server (server) --limit (limit) *make = (MAKE) --no-print-directory -f (self)se = '(subst ','\'',1)' The following command downloads fresh articles from all the newsgroups (from all the news servers above) to the current directory:  nntp2mboxawk '!/^#/ {print 1 ".newsgroup"}' /home/alex/.config/nntp2mbox/localhost.conf | grep '' | xargs -r /usr/bin/make --no-print-directory -f /home/alex/bin/nntp2mbox -Bk server=localhostawk '!/^#/ {print 1 ".newsgroup"}' /home/alex/.config/nntp2mbox/news.eternal-september.org.conf | grep '' | xargs -r /usr/bin/make --no-print-directory -f /home/alex/bin/nntp2mbox -Bk server=news.eternal-september.orgsinntp pull --server news.eternal-september.org --limit 500 comp.lang.cawk '!/^#/ {print 1 ".newsgroup"}' /home/alex/.config/nntp2mbox/news.gmane.io.conf | grep '' | xargs -r /usr/bin/make --no-print-directory -f /home/alex/bin/nntp2mbox -Bk server=news.gmane.iosinntp pull --server news.gmane.io --limit 500 gmane.comp.gnu.make.develsinntp pull --server news.gmane.io --limit 500 gmane.comp.gnu.make.generalsinntp pull --server news.gmane.io --limit 500 gmane.comp.window-managers.fvwm (Yes, it invokes Make recursively, which is a big no-no in many Make circles.)  lscomp.lang.c gmane.comp.gnu.make.generalgmane.comp.gnu.make.devel gmane.comp.window-managers.fvwm It even supports filtering by a newsgroup name:  nntp2mbox g=fvwm I don't actually read comp.lang.c. If there's anything sane left in comp.* hierarchy, please let me know. ## Thursday, December 10, 2020 ### Reading the Emacs User Survey 2020 Results More than a month ago some guy made a survey of emacs users. A couple of days ago, he released the results alongside with raw data. After importing Emacs-User-Survey-2020-clean.csv into sqlite (7,344 rows), the first thing I checked was if someone had mentioned any of my emacs packages &, I kid you not, I got 9 hits for wordnut! Yipee! Then I started filtering by "For how many years have you been using Emacs?" column. The amount of matched old-timers was staggering (I expected to find next to none): • >= 20 years: 1,497 rows • >= 15: 2,058 • >= 10: 2,975 Here's a tiny portion of interesting/hilarious entries: Usage (years) Favourite packages Community forums Difficulties while learning 42 Finnish Ispell usenet No cursor keys on ADM video terminal in 1978 God knows what is the ADM terminal & where did he get in Finland. Usage (years) Difficulties while learning Wishings 41 When I got booted into TECO, I was like WTF is this?? Did my modem disconnect? brain transplant Be written in Common Lisp 41 I often got stuck inside multiple ^R recursive edits but once I understood it was because of mini buffer exits I was ok. Nowadays it's less of an issue. 40 Get rid of "kill" from nomenclature, commands, etc. Why "kill-emacs" instead of "exit-emacs"? (I remapped that one decades ago; maybe it's changed in the district but my command still works). I dislike violence and wish kill buffers were no so named. This may seem minor, but if you don't think that language matters, perhaps you haven't heard the rants of the USA's current president. 40 Stop changing behaviors of next-line, search, etc. 39 Emacs' TECO macros were impenetrable. Nothing comes to mind. I drank the Kool-Aid a loong time ago. 35 stop styling my text with weird font lock crap. gets increasingly hard to turn off like to see vector drawing and variable width fonts in core 35 not really… the only difficulty is it wasn't on all the machines I used. I first used it on a VAX 11/780 having access to a bootleg hacked version crafted to run on the VAX under VMS. The guys that created that version were cad guys in DEC (AI CAD group) and I was lucky to have it… rather than being stuck with EDT. But then I started using an Apollo and lost it. So I tried to write my own Emacs. from scratch. I was sort of successful but who has that much extra time… then came the Sun and unix and the HP 9000 and then came Linux (finally) and I had Emacs most everywhere. 35 The learning curve is steep, but quick. I got nuthin. 34 my init file quickly became scrambled up because I pasted in code that I didn't understand nor know how to organize why did I have to figure out how to correctly compile emacs 27 on the latest ubuntu? Why wasn't a package immediately available on all OSs when 27.1 was released? You'd think that after 34 years of using Emacs, one would be able to discern Emacs maintainers from maintainers of the emacs package in their Linux distro, but no. Usage (years) Difficulties while learning Wishings 30 I liked it in the old days when, when you ran a repeating macro you would see all the changes zipping through on the screen. 29 Coming from Glosling Emacs, I didn't understand why ^T was so broken in GNU Emacs. In all honesty, when there's a new version of emacs I mostly spend time figuring out how to turn new abominations off or put them back to how they should be. It would be a big improvement for me if Emacs stuck to text and didn't try to do things with images, tables etc. When I paste anything into Emacs, it should either turn into text or fail. And more speed is always welcome. 27 This is 27 years ago and at that time everybody considered it cool to know emacs. So, no. Not really… I am professor in a computer science department and teach students. I provide for colleagues and friends a heavily adapted version of emacs that (I believe) is more user-friendly. Nevertheless, it is sad to see that students are not even interested anymore to learn emacs. So, I think the most pressing need is to have a simplified user interface that adheres to the usual standards (similar to what ergoemacs is trying to achieve). All basic functionality should be on menus, keyboard shortcuts should only be an add-on for power users. 26 Everything was hard. Copy paste. Saving files. The UI sort of sucks 25 Terrible, useless documentation 25 I don't understand elisp Responsiveness of the interface: Emacs sometimes feel slow. 23 More than 23 years ago, it was mandatory at my university. Key bindings and the like felt very alien. What some people think about poor Richard Stallman: • RMS should resign so that politics stops guiding Emacs development. It is a tragedy that a great editor continues to be crippled because technical decisions are made for outdated ideological reasons. I would love to contribute but Emacs development is extremely hostile to any non-purist views. • Stop letting RMS block good ideas. • There appears to be a split between the core developers and the "package" developers. I am confused by the role that RMS still plays in Emacs stewardship, and puzzled that he is not familiar with org-mode. • RMS's computing habits are so completely beyond what's normal that he has no idea what modern users want in an editor. If you want emacs to be popular you have to ACTUALLY LISTEN TO FEEDBACK FROM NEW USERS instead of a bunch of greybeards going "oh well emacs is fine for me". • I've considered dropping emacs altogether a few times because of RMS's behavior. The one thing I would like emacs to do is to stop having any affiliation with him. • Ignore RMS's opinions going forward. • … from reading the exchanges on the mailing list and especially RMS' opposition to anything "newfangled" has discouraged me from even trying to contribute to the core. Comments about the survey itself (I feel sorry for the guy who organized it): • I've refused to answer surveys that require proprietary JavaScript before. It's unacceptable for a community survey to demand cooperation with a corporation. I wouldn't've answered this survey were mailing this not an option. Of course, I'd issues sending this response, as I learned what server lay underneath the EMACSSURVEY.ORG domain MX records. It would be better if the SMTP servers were run differently, even by another business than that. • The last question on this page [What is the default keybinding to find a file?] stinks to high heaven. I don't know the answer, because my fingers do. But the real reason the last question stinks is that some doofus decided that answers placed there can only be some short number of characters, so I have to put my "I donoknow but mynfingers do" answer here instead of in that questions answer field. So I put a "nonsensical yet accurate" answer there. • Death to vi! • You are not enough experienced and whole survey is a joke with already set purposes, which we will find later. Would you be experienced you would know HTML, no Javascript is required. Would you be experienced, you would know who to hire, and not just linking to third party servers, thus exposing free software users to proprietary Javascript. Finally you are exposing their information to third party server which cannot be trusted. It is easy to edit few HTML elements and it would be to accept it over CGI and store in the database. I have rewritten the basic Perl form.cgi so many times for myself before 15+ years, and later wrote it for myself in Common Lisp, and I just wait for few free time to rewrite it in Emacs Lisp. All what you need is emacs CGI package and Emacs to prepare HTML. But I guess you are not getting what I am speaking about. • RMS did nothing wrong. • Is JotForm Free Software? • I disagree in the way the survey has been released without the emacs mantainers. • I had to disable no-script, so I'm angry. On a serious note, if you'd like to read what newbies really think of Emacs, filter "For how many years have you been using Emacs?" by 0, although it'll take a great deal of time (533 rows to examine). ## Tuesday, October 27, 2020 ### tex2png What do you do when you need to add a formula to a epub? Most epub readers don't support MathML yet, hence you resort to making SVGs via mathjax-node-cli. Then you test the epub in several über-popular readers to discover that only Kindle & Google Play Books render such SVGs correctly, the rest either loses all the characters in equations (KOReader) or just draws sad little boxes in place of the images (Moon+ Reader). How do you produce PNGs then? In the past, mathjax-node had an option of a png export, but it has been deprecated. There's a way to do it w/ pdflatex: (1) generate a pdf w/ the help of texlive-standalone package, (2) convert the pdf to a png. This doesn't sound complicated & it's not complicated, but there's no helpful wrappers available and if you want to integrate the tex→png process into your build pipeline, prepare to deal w/ the usual aux/log rubbish that any TeX program leaves around. Here's a makefile that does the conversion: #!/usr/bin/make -f(if (f),,(error "Usage: tex2png f='E=mc^2' output.png"))dpi := 600devnull := (if (findstring s, (MAKEFLAGS)),> /dev/null)%.pdf: pdflatex -jobname "(basename @)" -interaction=batchmode '\nofiles\documentclass[border=0.2pt]{standalone}\usepackage{amsmath}\usepackage{varwidth}\begin{document}\begin{varwidth}{\linewidth}$'(call se,(f))'$\end{varwidth}\end{document}' (devnull) @rm -f "(basename @).log"%.png: %.pdf gs -sDEVICE=pngalpha -dQUIET -dBATCH -dNOPAUSE -r(dpi) -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -sOutputFile="@" "<"se = '(subst ','\'',1)' It automatically removes all intermidiate files; iff you mistype a formula it saves a .log file to peruse. For example, render Parkinson's coefficient of inefficiency (published in ~1957):  ./tex2png -s output.png f='x = \frac{m^{o}(a-d)}{y + p\sqrt{b}}' (x = the number of members effectively present at the moment when the efficient working of a committee has become manifestly impossible; m = the average number of members actually present; o = the number of members influenced by outside pressure groups; a = the average age of the members; d = the distance in cm between the two members who are seated farthest from each other; y = the number of years since the cabinet or committee was first formed; p = the patience of the chairman, as measured on the Peabody scale; b = the average blood pressure of the three oldest members, taken shortly before the time of meeting.) ## Thursday, August 27, 2020 ### Steganography with zip archives The elegance of CVE-2020-1464 comes from the internal structure of Zip file format. While many other archive formats, like Microsoft Cab, put an index of the compressed files in the beginning of an archive, zip archivers place it in the end of a file. The reason is historical: apparently, in 1989 disk drives were so slow, that adding a new blob to an existing file & appending a new index to it was cheaper then copying chunks of the original archive to a new file. The CVE reminded me of an old joke of hiding a .zip in a .jpg. When you append a .zip to an image file, the recipient of the jpeg not necessarily notices junk in the image, but if you know about such a 'hidden' part, any ordinary unzip tool is able to extract it. This got me thinking: can we hide a file inside of a .zip? BlackHat Europe 2010 had a talk about steganography in popular archives formats. In one of the described tricks, carefully inserting a blob before a zip index, makes it invisible to all common unpackers. To verify this claim, I wrote a couple of small Ruby scripts, that inject & extract a 'hidden' blob. The approach works: Windows Explorer, 7-Zip, WinRAR, bsdtar(1), unzip(1) didn't see anything unusual. Even in the extreme cases like:  du -h foo.zip4.1G foo.zip bsdtar ftv foo.zip-rw-r--r-- 0 1000 100 1 Aug 25 21:58 q that certainly may look unusual to an innocent user–a 4 gigabyte archive that unpacks into an exactly 1 byte file! The opposite of a zip bomb. A Zip index is formally termed central directory. It consists of 2 main parts: ① central directory headers (CHDs) & ② end of central directory (EOCD) record. A CHD contain metadata about a particular file, EOCD–metadata about the index itself 1: class Eocd < BinData::Record endian :little uint32 :signature, asserted_value: 0x06054b50 uint16 :disk uint16 :disk_cd_start uint16 :cd_entries_disk uint16 :cd_entries_total uint32 :cd_size uint32 :cd_offset_start uint16 :comment_len string :comment, :read_length => :comment_len, onlyif: -> { comment_len.nonzero? }end The thing of interest here is cd_offset_start (officially called offset of start of central directory 2), a 4-byte value that indicates how many bytes to skip after the first file entry in an archive. Therefore, after inserting a blob, we need to update cd_offset_start, otherwise the zip file becomes broken. Just because a user has no clue about the hidden blob whatsoever, doesn't mean specialized tools won't notice it. Say, we have an archive w/ 2 text files:  bsdtar ft orig.zipThe Celebrated Jumping Frog of Calaveras County.txtWhat You Want.txt We inject a .png image to it:  zipography-inject orig.zip blob1.png > 1.zip Whilst bsdtar is still none the wiser:  bsdtar ft 1.zipThe Celebrated Jumping Frog of Calaveras County.txtWhat You Want.txt Hachoir correctly recognises it as an unparsed block: 1. This is a DSL from BinData package that provides a declarative way to read/write structured binary data in Ruby.↩︎ 2. Field names in PKWARE's spec are quite verbose.↩︎ ## Wednesday, July 22, 2020 ### How to build Ruby in Windows natively without WSL, MSYS2 or Cygwin Every Ruby release tarball contains file win32/README.win32. If you decide to distribute Ruby alongside your Windows app, you can either struggle with the instructions from that file or use MSYS2 (== the modern RubyInstaller). In the past there was Ruby-mswin32 project with an uninspiring motto The forever war against Windows ;-( [sic], but it has died of neglect. When you ask anyone knowledgeable about compiling Ruby under Windows, they oft (always?) say it's unbearably difficult to get right. On hearing that, I, of course, knew I was destined to repeat the endeavour. If you're going to do that blindly by installing VS2019 (the 'Community' edition, which is supposedly free) & by following the steps in win32/README.win32, you most probably come through, but end up with a crippled Ruby variant that has no openssl support whatsoever & hence you cannot run the gem command. Smashing. After wasting time on that I searched for a binary version of openssl suitable for the VS, recompiled Ruby to make sure rubygems was working & decided that the process was indeed getting mighty wearisome. Turns out, it can be simplified. At the time of writing, there's exactly 1 post on the interwebs about this topic by some Japanese guy on a Japanese knowledge community platform in Japanese. Instead of building/finding dependencies manually (we need at least 3 of them: openssl, readline & zlib) we can employ vcpkg for that job. I.e.: 1. Install VS2019. 2. Clone the vcpkg repo (say, to D:\opt\s\vcpkg). 3. Open x64 Native Tools Command Prompt for VS 2019. 4. Run bootstrap-vcpkg.bat inside the cloned vcpkg repo directory. 5. Download & compile the dependencies:  > vcpkg --triplet x64-windows install libxml2 libxslt openssl readline zlib 6. Set 3 env variables:  > set PATH=%PATH%;D:\opt\s\vcpkg\installed\x64-windows\bin > set INCLUDE=%INCLUDE%;D:\opt\s\vcpkg\installed\x64-windows\include > set LIB=%LIB%;D:\opt\s\vcpkg\installed\x64-windows\lib 7. cd to the unpacked Ruby src directory & type:  > win32\configure.bat --prefix=d:\opt\s\ruby then nmake & nmake install. If you did everything correctly, even irb should work: > irb -rfiddle -rfiddle/importirb(main):001:1* module User32irb(main):002:1* extend Fiddle::Importerirb(main):003:1* dlload 'user32'irb(main):004:1* extern 'int MessageBoxA(int, char*, char*, int)'irb(main):005:0> end=> #<Fiddle::Function:0x000000000676fbc8 ...>irb(main):006:0> User32::MessageBoxA 0, RUBY_DESCRIPTION, "", 0=> 1 By today's standards, the resulting full Ruby installation is pleasantly small:  du -shc vcpkg/installed/x64-windows/bin ruby/{bin,lib} --exclude '*.pdb' 7.0M vcpkg/installed/x64-windows/bin 2.5M ruby/bin 35M ruby/lib 45M total ## Saturday, April 18, 2020 ### Custom GIMP UI font size in Windows You'll need to create a new theme from an existing one. Here's an example for GIMP installed with scoop:  scoop list | grep gimp gimp 2.10.18 [extras] cd /cygdrive/c/Users/alex/scoop/apps/gimp/current/share/gimp/2.0/themes diff -ur System MySystemdiff -ur System/gtkrc MySystem/gtkrc--- System/gtkrc 2019-06-14 00:15:20.000000000 +0300+++ MySystem/gtkrc 2020-04-18 17:55:00.549251800 +0300@@ -38,7 +38,7 @@ # Uncommenting this line allows to set a different font for GIMP. #-# font_name = "sans 10"+ font_name = "segoe ui 12" GtkPaned::handle-size = 6 GimpDockWindow::default-height = 300@@ -104,3 +104,9 @@ } widget "*GimpDisplayShell.*" style "gimp-display-style"++style "my-menu-font"+{+ font_name="segoe ui 12"+}+widget_class "*Menu*" style "my-menu-font" Then start GIMP & open EditPreferencesInterfaceTheme. Before: After: ## Wednesday, March 4, 2020 ### Open a url selected from anywhere on your desktop I'm sure something like this exists in gnome/kde, but there is nothing for fvwm. The idea is quite simple: you select a url in a text editor, a terminal emulator or whatever, press a kbd shortcut & your default browser opens a new tab w/ it.  cat xprimary-xdg-open#!/bin/sh# open up to 5 urls from the 1st 10K of the current selection via xdg-openalert() { xmessage -center -button ok -default ok -timeout 2 "*"; }uri='^https?://...'idx=0; for line in xsel | head -c 10240 | tr '[:space:]' '\n' | egrep -a "uri" | head -5 | sort -u; do idx=1 xdg-open "line" || alert Failed to xdg-open \"line"\ &done[ idx -eq 0 ] && alert The PRIMARY selection contains no urls! (It requires xsel, xorg-x11-apps & xdg-utils Fedora packages.) Why so long a script if something like "xsel | xargs xdg-open" should suffice? • it reports if a selection was empty • it reports an error if xdg-open was unable to open a browser • you can open multiple urls concurrently • there is some protection against junk in the selection E.g.,  head -c ((1024*1024)) < /dev/urandom | xsel ./xprimary-xdg-open & you get a gui error message: instead of "omg, what was that" on the stderr. xmessage draws not exactly the prettiest dialog boxes, but who cares. The last "important" question here is what shortcut to choose? I decided upon Win-Shift-C, for in the past it opened the nonsensical "charms" menu in Windows (does nothing in 1909) & wretched Linux desktops, of course, have no charms. For ~/.fvwm/.fvwm2rc: Key c A S4 Exec exec xprimary-xdg-open ## Sunday, February 9, 2020 ### Building an rpm in the current directory without any build environment Say you have a proper .spec file, e.g. from a Fedora rpm repo. The repo contains the latest patches for the package but you won't get them via dnf this week b/c of the ~slow testing process. Or you just want to apply a small fix to a package. How to do this quickly w/o spending hours on a correct "build environment", "infrastructure", w/o re-reading Maximum RPM book & the wearisome packaging guidelines? Say we want to amend the flite Fedora package. At the time of writing, its .spec file describes not only a prehistoric version of the program but a configuration w/ the most robotic voices possible. Let's at least fix the latter. Clone https://src.fedoraproject.org/rpms/flite.git and apply the following patch to it: diff --git a/flite.spec b/flite.specindex dc6dd28..ccf8251 100644--- a/flite.spec+++ b/flite.spec@@ -3,2 +3,3 @@ Version: 1.3 Release: 35%{?dist}+Epoch: 1000 Summary: Small, fast speech synthesis engine (text-to-speech)@@ -52,3 +53,3 @@ cp -p %{SOURCE1} . autoreconf -vif-%configure --enable-shared --with-audio=alsa+%configure --enable-shared --with-audio=alsa --with-vox=cmu_us_kal16 # This package fails parallel make (thus cannot be built using "_smp_flags") To build an rpm, cd to the repo directory & run:  rpmbuild --load ~/lib/macros.spec -bb flite.spec... find -name \*rpm./_out/RPMS/x86_64/flite-devel-1.3-35.fc31.x86_64.rpm./_out/RPMS/x86_64/flite-1.3-35.fc31.x86_64.rpm That's it. It even automatically fetches a source tarball. The only missing part is a mysterious ~/lib/macros.spec: %_topdir %(pwd)/_out%_sourcedir %(pwd)%debug_package %nil%_disable_source_fetch %nil That file is your whole "rpm build environment". ## Wednesday, January 15, 2020 ### Automatic static assets dependency discovery In the past when I wanted to grab static files from node_modules directory I'd script it in a makefile like this: vendor.src := foo/bar.js foo/bar.cssvendor.dest := (addprefix (out)/vendor/, (vendor.src))(out)/vendor/%: node_modules/%; (copy)define copy =@mkdir -p (dir @)cp < @endef Then in some index.html I'd have: <script src="vendor/foo/bar.js"></script> & so on. This worked fine, but required a manual sync b/w the html file & its dependencies. Then it dawned upon me (slowpoke.webp) that the value of vendor.src variable could be discovered automatically; all we need here is a decent html parser that is usable from the command line. We can use nokogiri or a cli for cheerio library (disclamer). vendor.src := (shell adieu -pe '$$("link,script").map((_,e) => $$(e).attr("href") ||$$(e).attr("src")).get().filter(v => /node_modules/.test(v)).join\n' src/index.html)vendor.dest :=$(addprefix $(out)/,$(vendor.src))$(out)/node_modules/%: node_modules/%;$(copy)

vendor.src doesn't look very pretty any longer but now I can edit my index.html w/o worrying about the makefile.