Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add default.nix which can produce ps and pdf files #52

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ mathematical-companion/*.log
mathematical-companion/*.out
mathematical-companion/*.pdf
mathematical-companion/*.toc

result
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

.PHONY: clean check-nix check-git
.DEFAULT_GOAL = all

GIT_COMMIT = $(shell git rev-parse HEAD)
SOURCES = include/*.inc

all: check-nix check-git ${SOURCES}
@echo "Starting build with git commit ID: ${GIT_COMMIT}"
nix-build --argstr ref ${GIT_COMMIT}

clean:
rm result || true

check-git:
@command -v git > /dev/null 2>&1 || (echo "Must have git installed on your system." && exit 1)
@git rev-parse HEAD >/dev/null 2>&1 || (echo "Must be running inside of a git repository." && exit 1)

check-nix:
@command -v nix-build > /dev/null 2>&1 || (echo "Must have Nix installed on your system." && exit 1)
Binary file modified SSS32.ps
Binary file not shown.
251 changes: 251 additions & 0 deletions default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
{ pkgs ? import <nixpkgs> { }
, stdenv ? pkgs.stdenv
, lib ? pkgs.lib
, ghostscript ? pkgs.ghostscript
, ref ? null
, doPdfGeneration ? true
# Attempts to render every page individually to make sure the dependencies
# are set correctly.
, doCheck ? true
# Checks whether the generated output matches the checked-in SSS32.ps.
# When doing development you may want to shut this off to obtain the
# output file that you need to check in.
#
# Has no effect if doChecks is not set.
, doOutputDiff ? true
}:

let
src =
if isNull ref
then lib.sourceFilesBySuffices ./. [ ".ps" ".inc" ]
else
fetchGit {
url = ./.;
inherit ref;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reviewing https://nix.dev/manual/nix/2.22/language/builtins#builtins-fetchGit I'm pretty sure you want rev instead of ref.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empirically rev seems to want a 40-character hash, while ref will accept short hashes, branch names, etc.

};
shortId = lib.optionalString (! isNull ref) ("-" + builtins.substring 0 8 src.rev);

setup = rec {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably best to remove rec.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove it for now, but it will be used -- this setup set will contain all the various components of the postscript "setup" section. Each will have a dependencies array which refers to other setup components.

# Will be broken into multiple sub-files in a later PR.
fullSetup = {
content = builtins.readFile "${src}/include/setup.ps.inc";
dependencies = [ ];
};
};
# Dependencies that every page has
standardDependencies = [ setup.fullSetup ];

allPages = {
title = {
sourceHeader = "Title Page";
content = builtins.readFile "${src}/include/title.ps.inc";
dependencies = [ ];
skipPageNumber = true;
};
license = {
sourceHeader = "License Information";
content = builtins.readFile "${src}/include/license.ps.inc";
dependencies = [ ];
skipPageNumber = true;
};
reference = {
sourceHeader = "Reference Sheet";
content = builtins.readFile "${src}/include/reference.ps.inc";
dependencies = [ ];
drawFooter = true;
};
principalTables = {
sourceHeader = "Arithmetic Tables";
content = builtins.readFile "${src}/include/principal-tables.ps.inc";
dependencies = [ ];
};

additionBottom = {
content = "{xor} (Addition) code dup perm drawBottomWheelPage\n";
dependencies = [ ];
};
additionTop = {
content = "showTopWheelPage\n";
dependencies = [ ];
};
recovery = {
content = builtins.readFile "${src}/include/volvelle-recovery.ps.inc";
dependencies = [ ];
};
fusionInner = {
content = builtins.readFile "${src}/include/volvelle-fusion-1.ps.inc";
dependencies = [ ];
};
fusionOuter = {
content = builtins.readFile "${src}/include/volvelle-fusion-2.ps.inc";
dependencies = [ ];
};

generationInstructions = {
content = builtins.readFile "${src}/include/page7.ps.inc";
dependencies = [ ];
};

checksumTable1 = {
content = builtins.readFile "${src}/include/checksum-table-1.ps.inc";
isLandscape = true;
dependencies = [ ];
drawFooter = true;
};
checksumTable2 = {
content = builtins.readFile "${src}/include/checksum-table-2.ps.inc";
dependencies = [ ];
isLandscape = true;
drawFooter = true;
};
checksumWorksheet = {
content = builtins.readFile "${src}/include/checksum-worksheet.ps.inc";
dependencies = [ ];
isLandscape = true;
drawFooter = true;
};

shareTable = a: b: c: d: {
content = "${toString a} ${toString b} ${toString c} ${toString d} showShareTablePage\n";
dependencies = [ ];
drawFooter = true;
};
};

fullBooklet = {
name = "SSS32.ps";
pages = with allPages; [
title
license
reference
principalTables
additionBottom
additionTop
generationInstructions
(shareTable 29 24 13 25)
(shareTable 9 8 23 18)
(shareTable 22 31 27 19)
(shareTable 1 0 3 16)
(shareTable 11 28 12 14)
(shareTable 6 4 2 15)
(shareTable 10 17 21 20)
(shareTable 26 30 7 5)
recovery
fusionInner
fusionOuter
checksumTable1
checksumTable2
checksumWorksheet
];
};

dependencyContentRecur = content: builtins.concatMap
(item: (dependencyContentRecur item.dependencies) ++ [ item.content ])
content;
dependencyContent = pages: lib.lists.unique (
(map (dep: dep.content) standardDependencies) ++
(builtins.concatMap (page: dependencyContentRecur page.dependencies) pages)
);

renderBooklet = booklet:
let
addPage = content: pageData: {
content = content.content + lib.optionalString (pageData ? sourceHeader) ''
%****************************************************************
%*
%* ${pageData.sourceHeader}
%*
%****************************************************************
'' + ''
%%Page: ${toString content.nextPgIdx} ${toString content.nextPgIdx}
Copy link
Collaborator

@roconnor-blockstream roconnor-blockstream Apr 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this is for a follow up PR, but if you want to get fancy with page numbering then you need to update this line. This isn't just a pair of page numbers repeated for no reason. You'll have to look up the spec, but it is something like one is the literal sequence number and the other is the displayed page number. This makes a difference for assembling a sidebar of contents on your PS viewer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roconnor-blockstream yep, I'll address that in a future PR. Unfortunately ps2pdf loses this information (or maybe PDF doesn't support it) so it's of somewhat limited value.

${lib.optionalString (pageData ? isLandscape) "%%PageOrientation: Landscape\n"}
%%BeginPageSetup
/pgsave save def
%%EndPageSetup
${if pageData ? isLandscape then "landscapePage" else "portraitPage"} begin
${lib.optionalString (pageData ? drawFooter) "${toString content.nextFooterIdx} drawFooter"}
${pageData.content}
end
pgsave restore
showpage
'';
nextFooterIdx = content.nextFooterIdx + (if pageData ? drawFooter then 1 else 0);
nextPgIdx = content.nextPgIdx + 1;
};
initialContent = {
content = ''
%!PS-Adobe-3.0
%%Orientation: Portrait
%%Pages: ${toString (builtins.length booklet.pages)}
%%EndComments
%%BeginSetup
${toString (dependencyContent (booklet.pages))}%%EndSetup

%************************************************************************
%************************************************************************
%*
%* Section Three: Page Rendering
%*
%************************************************************************
%************************************************************************

'';
nextPgIdx = 1;
nextFooterIdx = 1;
};
finalContent = builtins.foldl' addPage initialContent booklet.pages;
in
pkgs.writeTextFile {
name = booklet.name;
text = finalContent.content + ''
%%EOF
'';
};
checkSinglePage = page: renderBooklet {
name = "test-single-page.ps";
pages = [ page ];
};
in
stdenv.mkDerivation {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just put renderBooklet as the derivation result? Wouldn't that make it easier to check the result into git? We can do nix-build -o SSS32.ps and then commit SSS32.ps. (I haven't actually tried this).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I eventually want to produce more than one output -- the full booklet, but also postscript files with individual worksheets, 256-bit vesrions of the worksheets, etc. And maybe a illustrated version alongside the regular version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On further reflection, and having learned Nix better, let me explore making this derivation have multiple outputs, where the default is the fully-assembled SSS32.ps. I'm unsure if this is the right way to go still, because there are e.g. flags we want to be able to set about PDF vs PS, color vs non-color etc, and it might still be the cleanest thing to output a directory with a bunch of different files.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My experiments with git and symlinks was a failure. While we could support multiple outputs, I do not think that it worth worrying about. At least not at this time.

name = "codex32${shortId}";

nativeBuildInputs = if doPdfGeneration then [ ghostscript ] else [ ];

phases = [ "buildPhase" ] ++ lib.optionals doCheck [ "checkPhase" ];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the checkPhase is never run because doCheck needs to be set to true in the stdenv.mkDerivation in order to run the checkPhase

You should change the phases line to:

phases = [ "buildPhase" "checkPhase" ];
inherit doCheck;

and let doCheck control whether or not the check phase is executed or not.


buildPhase = ''
set -e

FULL_BW="${renderBooklet fullBooklet}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way nix derivations work is that, more or less, every value in the derivation becomes an environment variable.

Therefore, it would make more sense for you to define FULL_BW as an entry in the derivation rather that setting it in the build phase.


# Copy output Postscript into place
mkdir "$out"
cd "$out"
cp "$FULL_BW" SSS32.ps
# Patch to include version
sed -i 's/(revision \(.*\))/(revision \1${shortId})/' ./SSS32.ps
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably needs to be more robust, but it is fine for now.

# Produce PDF, if requested.
${lib.optionalString doPdfGeneration "ps2pdf -dPDFSETTINGS=/prepress SSS32.ps"}
'';

checkPhase = toString
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the check fails when it is actually enabled, at least in part because you try to run ghostscriptTest before it is defined.

(map
(page: "ghostscriptTest ${checkSinglePage page}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another issue with this design. Because checkPhase contains references to the single pages, those single pages must be built, whether or not they will be checked. In this case I suggest taking the unusual step of conditionally excluding the entire contents of the checkPhase value if doCheck is false.

Alternatively you could replace fullBooklet.pages with [] when doCheck is false.

fullBooklet.pages
) + ''
ghostscriptTest() {
echo "Ghostscript testing $1"
local output;
if ! output="$(gs -dNOPAUSE -dNODISPLAY "$1" < /dev/null 2>&1)"; then
echo "Failed to run ghostscript on $1"
echo "$output"
exit 1
fi
}

ghostscriptTest "$FULL_BW"
${lib.optionalString doOutputDiff "diff -C 5 ${src}/SSS32.ps SSS32.ps"}
'';
}

11 changes: 11 additions & 0 deletions include/checksum-table-1.ps.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/Helvetica-Bold findfont 10 scalefont setfont
pageW 2 div pageH 48 sub moveto (MS32 Checksum Table) centreshow

/Courier findfont 8.5 scalefont setfont
36 pageH 64 sub % x y
pageW 64 sub pageH 148 sub 2 div % w h
0 drawChecksumTable

36 pageH 2 div 16 sub % x y
pageW 64 sub pageH 148 sub 2 div % w h
8 drawChecksumTable
11 changes: 11 additions & 0 deletions include/checksum-table-2.ps.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/Helvetica-Bold findfont 10 scalefont setfont
pageW 2 div pageH 48 sub moveto (MS32 Checksum Table) centreshow

/Courier findfont 8.5 scalefont setfont
36 pageH 64 sub % x y
pageW 64 sub pageH 148 sub 2 div % w h
16 drawChecksumTable

36 pageH 2 div 16 sub % x y
pageW 64 sub pageH 148 sub 2 div % w h
24 drawChecksumTable
79 changes: 79 additions & 0 deletions include/checksum-worksheet.ps.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/Helvetica-Bold findfont 10 scalefont setfont
pgsize aload pop exch pop 2 div 700
moveto (ms32 Checksum Worksheet) centreshow

gsave
50 560 translate
ladder begin
drawgrid
% (2NAMES5GS8YDXGMLUW34LEN0PRDAK9GLF307N04SN6SKL) fillgrid
% (2NAMES5GS8YDXGMLUW34LEN0PRDAK9GL ) fillgrid
end
grestore

100 320 moveto
/Helvetica-Bold findfont 10 scalefont setfont
(Verifying Checksums) show
100 300 moveto
/Helvetica findfont 9 scalefont setfont
(Write out the 45 character data portion in the) show
100 290 moveto
(bold boxes, two at a time, starting on the top) show
100 280 moveto
(row. Working from the top row down, look up) show
100 270 moveto
(the first two characters of each odd row in the) show
100 260 moveto
(ms32 Checksum Table and write the ) polymodulus length 10 string cvs concatstrings show
100 250 moveto
(character word into the even row below it. Fill) show
100 240 moveto
(in the odd rows by adding the two characters) show
100 230 moveto
(above each cell. You may use either the) show
100 220 moveto
(addition wheel table. The first few boxes are) show
100 210 moveto
(already filled in for you. The last row will sum) show
100 200 moveto
(to ) show checksumstring {glyphshow} forall ( if the checksum is valid.) show
100 160 moveto
/Helvetica-Bold findfont 10 scalefont setfont
(Creating Checksums) show
100 140 moveto
/Helvetica findfont 9 scalefont setfont
(Follow the "Verifying Checksums" instructions) show
100 130 moveto
(to fill in everything but the shaded cells. To fill in) show
100 120 moveto
(the shaded cells, write ) show checksumstring {glyphshow} forall ( into the bottom) show
100 110 moveto
(row. Working from the bottom up, fill in the) show
100 100 moveto
(shaded cells by adding the two characters below) show
100 90 moveto
(each cell. The ) polymodulus length 10 string cvs ( characters in the bold shaded) concatstrings concatstrings show
100 80 moveto
(boxes will be the checksum.) show

/offsety 550 def
/offsetx 450 def
/Courier findfont 10 scalefont setfont
20 offsetx add offsety moveto (Addition Table) show
/Courier-Bold findfont 8 scalefont setfont
0 1 31 {
dup 2 add 7 mul offsetx add offsety 10 sub moveto
perm exch get
code exch get glyphshow
} for

0 1 31 {
/Courier-Bold findfont 8 scalefont setfont
offsetx 34.5 7 mul add offsety 20 sub 2 index 8 mul sub moveto
dup code exch perm exch get get glyphshow
/Courier findfont 8 scalefont setfont
dup 1 31 {
dup 2 add 7 mul offsetx add offsety 20 sub 3 index 8 mul sub moveto
perm exch get
perm 2 index get gf32add code exch get glyphshow
} for pop } for
8 changes: 8 additions & 0 deletions include/license.ps.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/Helvetica findfont 6 scalefont setfont
marginX1 marginY1 16 sub moveto
MIT {gsave ((c)) search {show pop /copyright glyphshow} if show grestore 0 -8 rmoveto} forall
/Helvetica findfont 6 scalefont setfont
warning {gsave show grestore 0 -7 rmoveto} forall
0 -16 rmoveto
/Helvetica findfont 8 scalefont setfont
README {gsave show grestore 0 -10 rmoveto} forall
Loading