├── .github └── workflows │ ├── build.yml │ └── update_src.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── default.nix ├── emacs-overlay.nix ├── emacs-source ├── README.md ├── emacs-master.json ├── update └── update-emacs-source ├── note-to-self.md ├── patches ├── fix-window-role-yabai.patch └── no-titlebar.patch └── pkgs.nix /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [experiment/**] 6 | pull_request: 7 | branches: [main] 8 | workflow_run: 9 | workflows: ["Update Emacs Source"] 10 | branches: [main] 11 | types: 12 | - completed 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#example-workflow-configuration 16 | workflow_dispatch: 17 | 18 | jobs: 19 | build-all: 20 | runs-on: macos-latest 21 | steps: 22 | # setup nix and cachix environment 23 | - uses: actions/checkout@v2 24 | - uses: cachix/install-nix-action@v13 25 | - uses: cachix/cachix-action@v10 26 | with: 27 | name: emacs-osx 28 | authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" 29 | 30 | - name: Attempt nix-build 31 | run: nix-build 32 | 33 | - name: Build success, tag as "latest" 34 | if: ${{ success() }} 35 | run: | 36 | git fetch --all --tags 37 | git pull --rebase --autostash 38 | git tag -d built 39 | git push origin :refs/tags/built 40 | git tag built 41 | git push origin built 42 | -------------------------------------------------------------------------------- /.github/workflows/update_src.yml: -------------------------------------------------------------------------------- 1 | name: Update Emacs Source 2 | 3 | on: 4 | schedule: 5 | # At 4pm UTC (SG time midnight) past every 24th hour. 6 | - cron: "0 16 * * *" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | update-overlay: 11 | runs-on: macos-latest 12 | steps: 13 | - name: Checkout project 14 | uses: actions/checkout@v2 15 | 16 | - name: Install Nix 17 | uses: cachix/install-nix-action@v13 18 | with: 19 | nix_path: nixpkgs=channel:nixos-unstable 20 | 21 | - name: Configure Git 22 | run: | 23 | git config user.name github-actions 24 | git config user.email github-actions@github.com 25 | 26 | - name: Update inputs 27 | run: | 28 | ./emacs-source/update 29 | 30 | - name: Push commit with updated inputs 31 | run: | 32 | git pull --rebase --autostash 33 | git push 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /result* 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | nix-build | cachix push emacs-osx 3 | 4 | install: 5 | nix-env -f . -iA emacsOsxNative 6 | sudo rm -rf /Applications/Emacs.app 7 | sudo cp -rL ~/.nix-profile/Applications/Emacs.app /Applications 8 | 9 | install-with-tile: 10 | nix-env -f . -iA emacsOsxNativeTile 11 | sudo rm -rf /Applications/Emacs.app 12 | sudo cp -rL ~/.nix-profile/Applications/Emacs.app /Applications 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emacs OSX 2 | 3 | ![Latest HEAD Emacs Status](https://github.com/sagittaros/emacs-osx/actions/workflows/build.yml/badge.svg) 4 | 5 | 6 | ## Features 7 | 8 | - Tested on Mac OSX Big Sur and Catalina 9 | - Works with yabai tiling window manager 10 | - Comes with 4 variants (with/without native-compilation, and with/without tiling support) 11 | - Cached builds, updated from source every 24 hours 12 | 13 | ## There are 4 variants 14 | 15 | #### emacsOsx 16 | 17 | > master emacs. Fresh out of https://savannah.gnu.org/projects/emacs 18 | 19 | #### emacsOsxNative 20 | 21 | > master emacs, with native compilation 22 | 23 | #### emacsOsxTile 24 | 25 | > emacsOsx with tiling support (may not work with posframe/childframe) 26 | 27 | #### emacsOsxNativeTile 28 | 29 | > emacsOsxNative with tiling support (may not work with posframe/childframe) 30 | 31 | ## Installation 32 | 33 | #### Install Nix and optionally nix-darwin 34 | 35 | No Nix knowledge required, and you do NOT need `nix-darwin` for this to work. [Nix Darwin](https://github.com/LnL7/nix-darwin) allows installation to /Applications folder but you could also symlink from `~/.nix-profile/Applications`. 36 | 37 | Visit this link if you haven't installed Nix already. 38 | https://nixos.org/guides/install-nix.html 39 | 40 | #### Install to nix-store 41 | 42 | ```sh 43 | # Install from cachix binaries 44 | nix-env -iA cachix -f https://cachix.org/api/v1/install 45 | cachix use emacs-osx 46 | 47 | # `emacsOsxNative` can be substituted with any variant (mentioned above) 48 | nix-env -iA emacsOsxNative -f https://github.com/sagittaros/emacs-osx/archive/refs/tags/built.tar.gz 49 | ``` 50 | 51 | #### Copy to /Applications 52 | 53 | Not required if you are using nix-darwin. 54 | 55 | ```sh 56 | sudo rm -rf /Applications/Emacs.app 57 | sudo cp -rL ~/.nix-profile/Applications/Emacs.app /Applications 58 | ``` 59 | 60 | ## Motivation 61 | 62 | - Replicate [Emacs Overlay](https://github.com/nix-community/emacs-overlay) to OSX/darwin environment 63 | - Allow overrides of patches 64 | - Last but not least, for my own learning and use 65 | - And in future (my own custom icons) 66 | 67 | ## Patches 68 | 69 | More patches can be found below: 70 | 71 | - [daviderestivo's emacs-head](https://github.com/daviderestivo/homebrew-emacs-head/tree/master/patches) 72 | - [Emacs plus](https://github.com/d12frosted/homebrew-emacs-plus/tree/master/patches/emacs-28) 73 | - [Emacs overlay](https://github.com/nix-community/emacs-overlay/tree/master/patches) 74 | 75 | Note that this uses --with-ns (nextstep) and patches from "Mac Port" (see [here](https://bitbucket.org/mituharu/emacs-mac/src/master/)) version will likely not work. 76 | 77 | ## References 78 | 79 | - https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/applications/editors/emacs/generic.nix 80 | - https://github.com/nix-community/emacs-overlay 81 | - https://european-lisp-symposium.org/static/2020/corallo-nassi-manca-slides.pdf 82 | - https://arxiv.org/pdf/2004.02504.pdf 83 | - https://www.gnu.org/software/emacs/manual/html_node/elisp/Building-Emacs.html 84 | 85 | ## Inspired By 86 | 87 | [Gccemacs by twlz0ne](https://github.com/twlz0ne/nix-gccemacs-darwin) 88 | 89 | ## CI configuration (Github Action) 90 | 91 | Refer https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on 92 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | let pkgs = import ./pkgs.nix; 2 | in with pkgs; { 3 | inherit emacsOsx emacsOsxNative; 4 | 5 | # for use in chunwm or yabai 6 | inherit emacsOsxTile emacsOsxNativeTile; 7 | } 8 | -------------------------------------------------------------------------------- /emacs-overlay.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import ./pkgs.nix; 3 | 4 | # I am using the "builder" from nix-community's emacs-overlay, with some slight modifications 5 | # https://github.com/nix-community/emacs-overlay/blob/d1fbf6d39f3a0869c5fb0cc7f9ba7c9033e35cf9/default.nix#L25 6 | mkGitEmacs = namePrefix: jsonFile: patches: 7 | { ... }@args: 8 | let 9 | repoMeta = pkgs.lib.importJSON jsonFile; 10 | fetcher = if repoMeta.type == "savannah" then 11 | pkgs.fetchFromSavannah 12 | else if repoMeta.type == "github" then 13 | pkgs.fetchFromGitHub 14 | else 15 | throw "Unknown repository type ${repoMeta.type}!"; 16 | in builtins.foldl' (drv: fn: fn drv) pkgs.emacs [ 17 | 18 | (drv: drv.override ({ srcRepo = true; } // args)) 19 | 20 | # in order for this build to be differentiated from original `nixpkgs.emacs` 21 | (drv: 22 | drv.overrideAttrs (old: { 23 | name = "${namePrefix}-${repoMeta.version}"; 24 | inherit (repoMeta) version; 25 | inherit patches; 26 | src = fetcher (builtins.removeAttrs repoMeta [ "type" "version" ]); 27 | postPatch = old.postPatch + '' 28 | substituteInPlace lisp/loadup.el \ 29 | --replace '(emacs-repository-get-version)' '"${repoMeta.rev}"' \ 30 | --replace '(emacs-repository-get-branch)' '"master"' 31 | ''; 32 | # https://github.com/NixOS/nixpkgs/issues/109997#issuecomment-867318377 33 | CFLAGS = "-DMAC_OS_X_VERSION_MAX_ALLOWED=110200 -g -O2"; 34 | })) 35 | ]; 36 | 37 | emacsOsx = mkGitEmacs "emacs-osx" ./emacs-source/emacs-master.json [ ] { }; 38 | 39 | emacsOsxNative = mkGitEmacs "emacs-osx" ./emacs-source/emacs-master.json [ ] { 40 | nativeComp = true; 41 | }; 42 | 43 | emacsOsxTile = mkGitEmacs "emacs-osx" ./emacs-source/emacs-master.json [ 44 | ./patches/no-titlebar.patch 45 | ./patches/fix-window-role-yabai.patch 46 | ] { }; 47 | 48 | emacsOsxNativeTile = mkGitEmacs "emacs-osx" ./emacs-source/emacs-master.json [ 49 | ./patches/no-titlebar.patch 50 | ./patches/fix-window-role-yabai.patch 51 | ] { nativeComp = true; }; 52 | 53 | in _: _: { 54 | inherit emacsOsx; 55 | inherit emacsOsxNative; 56 | 57 | # for use in chunwm or yabai 58 | inherit emacsOsxTile; 59 | inherit emacsOsxNativeTile; 60 | } 61 | -------------------------------------------------------------------------------- /emacs-source/README.md: -------------------------------------------------------------------------------- 1 | # Refer 2 | 3 | https://git.savannah.gnu.org/cgit/emacs.git 4 | 5 | This will download the latest master revision of emacs to JSON file. 6 | 7 | Branch 27 is commented out but can be enabled. -------------------------------------------------------------------------------- /emacs-source/emacs-master.json: -------------------------------------------------------------------------------- 1 | {"type": "savannah", "repo": "emacs", "rev": "0ee1a46e6c7fa159584a9c04f5ab9bf694d6de3b", "sha256": "1fxfjilm4x1pgnvg5py0p1x90c4hsfsnjsdls5bmxh9hq9md10ak", "version": "20211212.0"} 2 | -------------------------------------------------------------------------------- /emacs-source/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #! nix-shell -i bash -p bash git 3 | 4 | # update source, and if source changes, create a new commit 5 | ./emacs-source/update-emacs-source && (git diff --exit-code emacs-source > /dev/null || git commit -m "Updated emacs" -- emacs-source) 6 | 7 | exit 0 -------------------------------------------------------------------------------- /emacs-source/update-emacs-source: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #! nix-shell -i bash -p curl xmlstarlet nix coreutils 3 | set -euxo pipefail 4 | 5 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 6 | cd $SCRIPTPATH 7 | 8 | function update_savannah_branch() { 9 | branch=$1 10 | echo emacs $branch 11 | 12 | # Get relevant data (commit id and timestamp) for the latest commit 13 | commit_data=$(curl "https://git.savannah.gnu.org/cgit/emacs.git/atom/?h=$branch" | xmlstarlet sel -N atom="http://www.w3.org/2005/Atom" -t -m /atom:feed/atom:entry -v "concat(atom:id,'/',atom:updated)" -n | head -n 1) 14 | 15 | # Extract commit sha and build a version number based on date: YYYYMMDD.0 16 | commit_sha=$(echo $commit_data | cut -d '/' -f 1) 17 | version_number=$(echo $commit_data | cut -d '/' -f 2 | cut -d 'T' -f 1 | sed 's/-//g').0 18 | 19 | output_branch=$(echo $branch | sed s/"\/"/"_"/) 20 | digest=$(nix-prefetch-url --unpack "https://git.savannah.gnu.org/cgit/emacs.git/snapshot/emacs-${commit_sha}.tar.gz") 21 | echo "{\"type\": \"savannah\", \"repo\": \"emacs\", \"rev\": \"${commit_sha}\", \"sha256\": \"${digest}\", \"version\": \"${version_number}\"}" > emacs-$output_branch.json 22 | } 23 | 24 | function update_release() { 25 | echo emacs release 26 | 27 | git clone --shallow-since="$(date --date='-6 month')" https://git.savannah.gnu.org/git/emacs.git emacs_git_repo 28 | tag=$(git -C emacs_git_repo tag -l --sort=-v:refname 'emacs-*' | grep -v '\-rc' | head -n1) 29 | rm -rf emacs_git_repo 30 | 31 | digest=$(nix-prefetch-url --unpack "https://git.savannah.gnu.org/cgit/emacs.git/snapshot/emacs-${tag}.tar.gz") 32 | version_number=$(echo $tag | cut -d '-' -f 2) 33 | 34 | echo "{\"type\": \"savannah\", \"repo\": \"emacs\", \"rev\": \"${tag}\", \"sha256\": \"${digest}\", \"version\": \"${version_number}\"}" > emacs-release.json 35 | } 36 | 37 | # master branch, at the time of writing, branch 28 38 | update_savannah_branch master 39 | 40 | # release branch, at 2021/04/28, this is branch 27.2 41 | # update_release 42 | -------------------------------------------------------------------------------- /note-to-self.md: -------------------------------------------------------------------------------- 1 | # Note to self 2 | 3 | Can we cache intermediate builds? 4 | 5 | ```sh 6 | 7 | # view all emacs-osx derivation 8 | ls /nix/store/ | rg emacs-osx 9 | 10 | 11 | # push upstream derivation 12 | nix-store -qR --include-outputs /nix/store/q2h55mkrcpk9paxibr3kc7n155cwyz8n-emacs-osx-20210428.0.drv | cachix push emacs-osx 13 | ``` 14 | -------------------------------------------------------------------------------- /patches/fix-window-role-yabai.patch: -------------------------------------------------------------------------------- 1 | From 614bc763e72bdd5b26add04338cacc6803e2a0d6 Mon Sep 17 00:00:00 2001 2 | From: Golem 3 | Date: Thu, 9 Jan 2020 07:22:17 +0200 4 | Subject: [PATCH] [patch] fix-window-role 5 | 6 | --- 7 | src/nsterm.m | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/src/nsterm.m b/src/nsterm.m 11 | index 6f9b208953..aa6c1d286f 100644 12 | --- a/src/nsterm.m 13 | +++ b/src/nsterm.m 14 | @@ -8768,7 +8768,7 @@ - (id)accessibilityAttributeValue:(NSString *)attribute 15 | NSTRACE ("[EmacsWindow accessibilityAttributeValue:]"); 16 | 17 | if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 18 | - return NSAccessibilityTextFieldRole; 19 | + return NSAccessibilityWindowRole; 20 | 21 | if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute] 22 | && curbuf && ! NILP (BVAR (curbuf, mark_active))) 23 | -- 24 | 2.26.2 25 | -------------------------------------------------------------------------------- /patches/no-titlebar.patch: -------------------------------------------------------------------------------- 1 | 2 | From 1e0a3fd9c7b9507e7820c3308a8183be7acab019 Mon Sep 17 00:00:00 2001 3 | From: Masahiro Nakamura 4 | Date: Fri, 27 Aug 2021 01:12:07 +0900 5 | Subject: [PATCH] no-titlebar-head 6 | 7 | --- 8 | src/nsterm.m | 6 +----- 9 | 1 file changed, 1 insertion(+), 5 deletions(-) 10 | 11 | diff --git a/src/nsterm.m b/src/nsterm.m 12 | index 1c1f0c8f23..13138b3339 100644 13 | --- a/src/nsterm.m 14 | +++ b/src/nsterm.m 15 | @@ -433,7 +433,7 @@ - (NSColor *)colorUsingDefaultColorSpace 16 | 17 | /* These flags will be OR'd or XOR'd with the NSWindow's styleMask 18 | property depending on what we're doing. */ 19 | -#define FRAME_DECORATED_FLAGS (NSWindowStyleMaskTitled \ 20 | +#define FRAME_DECORATED_FLAGS (NSWindowStyleMaskBorderless \ 21 | | NSWindowStyleMaskResizable \ 22 | | NSWindowStyleMaskMiniaturizable \ 23 | | NSWindowStyleMaskClosable) 24 | @@ -8293,10 +8293,6 @@ - (instancetype) initWithEmacsFrame:(struct frame *)f 25 | if ([col alphaComponent] != (EmacsCGFloat) 1.0) 26 | [self setOpaque:NO]; 27 | 28 | - /* toolbar support */ 29 | - if (! FRAME_UNDECORATED (f)) 30 | - [self createToolbar:f]; 31 | - 32 | /* macOS Sierra automatically enables tabbed windows. We can't 33 | allow this to be enabled until it's available on a Free system. 34 | Currently it only happens by accident and is buggy anyway. */ 35 | -- 36 | 2.28.0 37 | -------------------------------------------------------------------------------- /pkgs.nix: -------------------------------------------------------------------------------- 1 | let 2 | # refer https://status.nixos.org for latest nixpkgs 3 | commit = "b2d256095aeac09f8af6b6c95ab3cf4bc5fd4e6f"; 4 | pkgs = import (fetchTarball { 5 | url = "https://github.com/NixOS/nixpkgs/archive/${commit}.tar.gz"; 6 | 7 | # nix-prefetch-url --unpack \ 8 | # https://github.com/NixOS/nixpkgs/archive/b2d256095aeac09f8af6b6c95ab3cf4bc5fd4e6f.tar.gz 9 | sha256 = "06xamgm7w9xnn17lzg5kcmibch6a753rjc2xpf0z7jgw84rslff3"; 10 | }) { overlays = [ (import ./emacs-overlay.nix) ]; }; 11 | in pkgs 12 | --------------------------------------------------------------------------------