From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms0.migadu.com with LMTPS id ONQ8BxA552C1AwEAgWs5BA (envelope-from ) for ; Thu, 08 Jul 2021 19:42:40 +0200 Received: from aspmx1.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id oOvzAhA552ALGAAAB5/wlQ (envelope-from ) for ; Thu, 08 Jul 2021 17:42:40 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 5EBB8F987 for ; Thu, 8 Jul 2021 19:42:39 +0200 (CEST) Received: from localhost ([::1]:41928 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1m1Y2n-0000K6-E6 for larch@yhetil.org; Thu, 08 Jul 2021 13:42:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:49752) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1m1XcY-0005rh-Sp for emacs-orgmode@gnu.org; Thu, 08 Jul 2021 13:15:30 -0400 Received: from mx1.riseup.net ([198.252.153.129]:35762) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1m1XcW-0002QS-Eu for emacs-orgmode@gnu.org; Thu, 08 Jul 2021 13:15:30 -0400 Received: from fews1.riseup.net (fews1-pn.riseup.net [10.0.1.83]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (Client CN "*.riseup.net", Issuer "Sectigo RSA Domain Validation Secure Server CA" (not verified)) by mx1.riseup.net (Postfix) with ESMTPS id 4GLNGP5Nh5zDq9C; Thu, 8 Jul 2021 10:15:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=riseup.net; s=squak; t=1625764525; bh=sV7fnLN7CkpnqOq+zcM3jImogFVFm9Re4xcOseJyOms=; h=References:From:To:Cc:Subject:Date:In-reply-to:From; b=OB7abrFwL/6d6N3+yl6cwMYcgPv/QGDciUYtNnS7vNiAUjproCmsUVxlwMNOquw/5 1G9ojCZFDOym8x6rw07Cut2iWJcRgF2m2Xycv8vMzNIjBd34ge/QyZZPka3UwIHqGD aO2gcwBCzL4fjIceNPvYquyz8WtKIFZlF0W5wOcc= X-Riseup-User-ID: 7B0A933722431B5134FE48A9C234E28A94733FA3F73261AD5C33A3E00A8D4FD1 Received: from [127.0.0.1] (localhost [127.0.0.1]) by fews1.riseup.net (Postfix) with ESMTPSA id 4GLNGM67YJz5vcW; Thu, 8 Jul 2021 10:15:23 -0700 (PDT) References: <-0ZoEP_lzUvrnWSq9TwiYHNJ0Spa94xjiTOF0TU8np0pYgHEPx-62_dr5xBMd3VUu7frSRXxiAFje99v2jeaJg==@protonmail.internalid> <6nablMuhQkeviB-6VFvbgkSFIQUX0t1Cn9X8VKPNkMNmTx_r0NlLhnpTJQyUB9EjTZEtB_1xnumcRgd_UEl_mQ==@protonmail.internalid> <87h7h7nakd.fsf@gmail.com> <3116936.aeNJFYEL58@bl4ckspoons> <87r1g9lmp3.fsf@gmail.com> From: Trust me I am a Doctor To: Tom Gillespie Subject: Re: [PATCH] Allow tangling to a list of files Date: Thu, 08 Jul 2021 18:41:50 +0200 In-reply-to: Message-ID: <87k0m0682j.fsf@riseup.net> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=198.252.153.129; envelope-from=pillule@riseup.net; helo=mx1.riseup.net X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Tim Cross , emacs-orgmode@gnu.org, Jacopo De Simoi Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: "Emacs-orgmode" X-Migadu-Flow: FLOW_IN ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1625766159; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=JEKNSQ/cbFb/19Rdv4Tn1p0MOahslJETxRMSyhP78+g=; b=BuvLkhgzveTa+6oV/x6zSJ5ta84EYljmRfD6r8xYtKeFwwlf+MQyfaE3xhXlluleJDHHMK 9ruBj0/nV5SoLy3yuyBj98eDHy4km1w8mm+iZ/YOawg3ZJEQm1qJYMnPcb8EALM4mQ60MJ UygYqmjLDzXgrC1B+Oep9prVMmoa8M1DBfCClhvIh/zWbClAQxoQkUgNiz0VKqHiEppoNM BnGYmv5FtmKmDRo1CTVt88f2hxipHJtLn6LHXUeGg9vvDq+Xge4fJDEVY5gvWOEGE7Uayt EovJhpDollHCSg+2xO/8pHs+B9OvaQOMlVLopPAeazTud+9a4Z0jBS5/xMoleA== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1625766159; a=rsa-sha256; cv=none; b=OyGyLLZ1DaWtTapZSJdpgwbrnx0QMx5zsHKdd4WAzJHpxt3znWEs2tAyxnUQcgWwti123a diQNbJv3Y5hs1BpJ/xBEV+Y/6zXrO7BgpfBYKhuu8qMygEfEvy/OK6kXSsN5Z5VTjYkI8g 04Il786mfqvyjv8pyjAq0kJL6r1wZQNYX+FuTjMzP6+NR+4g4BVrk3SoX5HZSX4VhG+xoi f/siJoplpKz3+pqzkptKRBLR98ULnhC7faY0w3nD5oPP4EuS2giwqwNt2zKsWsWGNToyJI yaNIGbjz+nTrW7QztpmtYFd6Dc8uO3p6MB2T0U95jUwNVr1gvC8YpL5DLzKcLw== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=riseup.net header.s=squak header.b=OB7abrFw; spf=pass (aspmx1.migadu.com: domain of emacs-orgmode-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=emacs-orgmode-bounces@gnu.org X-Migadu-Spam-Score: -1.21 Authentication-Results: aspmx1.migadu.com; dkim=fail ("headers rsa verify failed") header.d=riseup.net header.s=squak header.b=OB7abrFw; dmarc=fail reason="SPF not aligned (relaxed)" header.from=riseup.net (policy=none); spf=pass (aspmx1.migadu.com: domain of emacs-orgmode-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=emacs-orgmode-bounces@gnu.org X-Migadu-Queue-Id: 5EBB8F987 X-Spam-Score: -1.21 X-Migadu-Scanner: scn0.migadu.com X-TUID: R92QePkXz7zi --=-=-= Content-Type: text/plain Hi, I have no particular opinion for the patch proposed but wanted to share with you some reflections I had on the subject to use one org file to tangle to multiple setup. I use abundantly virtual machines and my emacs configurations have many bits that I don't want everywhere, be it a mail setup, a rss setup, or extra languages setup ... etc. It is not exactly the same usage, I do not tangle different versions in one pass, I tangle a version at a time, and take care of installing it in the right place later. Naively I started to use the header's tags to tangle blocks. I thought because there was already in place a query syntax for the tags it may been interesting to use that. You can see attached a proto of a tangle function that accept tags specification and that will only tangle the blocks of the headings matching this specification; and some utilities that I use with it. --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=tag-tangler.el Content-Transfer-Encoding: quoted-printable Content-Description: tag-tangler.el (defun pils/org-babel-tangle (&optional arg target-file lang-re tag-query) "Write code blocks to source-specific files. Extract the bodies of all source code blocks from the current file into their own source-specific files. With one universal prefix argument, only tangle the block at point. When two universal prefix arguments, only tangle blocks for the tangle file of the block at point. Optional argument TARGET-FILE can be used to specify a default export file for all source blocks. Optional argument LANG-RE can be used to limit the exported source code blocks by languages matching a regular expression." (interactive "P") (run-hooks 'org-babel-pre-tangle-hook) ;; Possibly Restrict the buffer to the current code block (save-restriction (save-excursion (when (equal arg '(4)) (let ((head (org-babel-where-is-src-block-head))) (if head (goto-char head) (user-error "Point is not in a source code block")))) (let ((block-counter 0) (org-babel-default-header-args (if target-file (org-babel-merge-params org-babel-default-header-args (list (cons :tangle target-file))) org-babel-default-header-args)) (tangle-file (when (equal arg '(16)) (or (cdr (assq :tangle (nth 2 (org-babel-get-src-block-info = 'light)))) (user-error "Point is not in a source code block")))) path-collector) (mapc ;; map over all languages (lambda (by-lang) (let* ((lang (car by-lang)) (specs (cdr by-lang)) (ext (or (cdr (assoc lang org-babel-tangle-lang-exts)) la= ng)) (lang-f (org-src-get-lang-mode lang)) she-banged) (mapc (lambda (spec) (let ((get-spec (lambda (name) (cdr (assoc name (nth 4 spec= )))))) (let* ((tangle (funcall get-spec :tangle)) (she-bang (let ((sheb (funcall get-spec :shebang))) (when (> (length sheb) 0) sheb))) (tangle-mode (funcall get-spec :tangle-mode)) (base-name (cond ((string=3D "yes" tangle) (file-name-sans-extension (nth 1 spec))) ((string=3D "no" tangle) nil) ((> (length tangle) 0) tangle))) (file-name (when base-name ;; decide if we want to add ext to ba= se-name (if (and ext (string=3D "yes" tangle)) (concat base-name "." ext) base-n= ame)))) (when file-name ;; Possibly create the parent directories for file. (let ((m (funcall get-spec :mkdirp)) (fnd (file-name-directory file-name))) (and m fnd (not (string=3D m "no")) (make-directory fnd 'parents))) ;; delete any old versions of file (and (file-exists-p file-name) (not (member file-name (mapcar #'car path-collec= tor))) (delete-file file-name)) ;; drop source-block to file (with-temp-buffer (when (fboundp lang-f) (ignore-errors (funcall lang= -f))) (when (and she-bang (not (member file-name she-bang= ed))) (insert (concat she-bang "\n")) (setq she-banged (cons file-name she-banged))) (org-babel-spec-to-string spec) ;; We avoid append-to-file as it does not work with= tramp. (let ((content (buffer-string))) (with-temp-buffer (when (file-exists-p file-name) (insert-file-contents file-name)) (goto-char (point-max)) ;; Handle :padlines unless first line in file (unless (or (string=3D "no" (cdr (assq :padline= (nth 4 spec)))) (=3D (point) (point-min))) (insert "\n")) (insert content) (write-region nil nil file-name)))) ;; if files contain she-bangs, then make the executab= le (when she-bang (unless tangle-mode (setq tangle-mode 493))) ;; update counter (setq block-counter (+ 1 block-counter)) (unless (assoc file-name path-collector) (push (cons file-name tangle-mode) path-collector))= )))) specs))) (if (equal arg '(4)) (org-babel-tangle-single-block 1 t) ;;; dirty hands (let ((heading-list (org-scan-tags '(org-element-property :raw-value (org-ele= ment-at-point)) (cdr (org-make-tags-matcher tag-query t)) = nil)) (block-list (org-babel-tangle-collect-blocks))) (mapcar (lambda (lang) () (mapcar (lambda (block) (unless (member ; the heading information in block = list (car (split-string (nth 3 block) ":")) heading-list) (setf (cdr lang) (delq block (cdr lang))))) (cdr lang))) block-list) block-list))) ;;; dirty hands (message "Tangled %d code block%s from %s" block-counter (if (=3D block-counter 1) "" "s") (file-name-nondirectory (buffer-file-name (or (buffer-base-buffer) (current-buffer))))) ;; run `org-babel-post-tangle-hook' in all tangled files (when org-babel-post-tangle-hook (mapc (lambda (file) (org-babel-with-temp-filebuffer file (run-hooks 'org-babel-post-tangle-hook))) (mapcar #'car path-collector))) ;; set permissions on tangled files (mapc (lambda (pair) (when (cdr pair) (set-file-modes (car pair) (cdr pair)))) path-collector) (mapcar #'car path-collector))))) (defcustom pils-tag-list "pils-mail-elfeed" "String of org-mode tags to pass to the pils/org-babel-tangle in order to= tangle the blocks of a file relatively to the tags of their heading.") (require 'f) (defun pils-list-org-config-files () "List all org files in =E2=80=98user-emacs-directory=E2=80=99." (cl-remove-if-not (lambda (it) (string=3D "org" (f-ext it))) (f-files user-emacs-directory))) (defvar pils--tangling-file nil "Record the file being tangled in order to make headers in the destinatio= n file.") (defun pils-post-tangle () (when (eq major-mode 'emacs-lisp-mode) (goto-char (point-min)) (insert (format ";;; %s -*- lexical-binding: t; -*-" (file-name-nondirectory (buffer-file-name))) ;; if pils--tangling-file is set we expect to be tangling with = tags (if pils--tangling-file (format " ;; NOTE: this file is generated from %s ;; with the following tags : ;; %s " pils--tangling-file pils-tag-list) " ")) (save-buffer))) (defun pils/default-tangle () (let ((pils--tangling-file (file-name-nondirectory (buffer-file-name))) (org-babel-post-tangle-hook #'pils-post-tangle)) (when (and (>=3D emacs-major-version 27) (fboundp #'package-quickstart-= refresh) (add-hook 'after-save-hook #'package-quickstart-refresh 100= t))) (pils/org-babel-tangle nil nil nil pils-tag-list))) (defun pils/tangle-all-org-config (pils-tag-list) (let ((initial-buffer (current-buffer))) (cl-loop for FILE in (pils-list-org-config-files) do (let ((opened (get-buffer (file-name-nondirectory FILE)))) (find-file FILE) (let ((after-save-hook nil) (pils--tangling-file FILE) (org-babel-post-tangle-hook #'pils-post-tangle)) (pils/org-babel-tangle nil nil nil pils-tag-list)) (unless opened (kill-buffer opened)))) (when (fboundp #'package-quickstart-refresh) (package-quickstart-refres= h)) (switch-to-buffer initial-buffer))) --=-=-= Content-Type: text/plain However I must say that solution is quite simplistic. It effectively allow me to filter dynamically what I tangle from my files, but I cannot rely on it to specify elegantly, eg, complex dependencies between packages. At some point I wished to use org-properties to abstract some logics from Elisp to org, and then forget about I always have something else to tweak ... -- --=-=-=--