Skip to content

Conversation

@cyanogilvie
Copy link

@cyanogilvie cyanogilvie commented Nov 25, 2025

Add libtommath 1.3.0

@cyanogilvie cyanogilvie force-pushed the libtommath branch 10 times, most recently from 76aff6e to cb781a7 Compare November 26, 2025 10:16
@cyanogilvie cyanogilvie marked this pull request as draft November 26, 2025 10:55
@cyanogilvie cyanogilvie marked this pull request as ready for review November 26, 2025 12:16
Copy link
Collaborator

@bgilbert bgilbert left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

@cyanogilvie cyanogilvie force-pushed the libtommath branch 16 times, most recently from 6ab46e1 to 1c57d84 Compare November 29, 2025 11:00
@cyanogilvie
Copy link
Author

After extensive hacking the build and tests succeed on Windows MSVC and clang-cl, so I've enabled them in the CI

@cyanogilvie cyanogilvie requested a review from bgilbert November 29, 2025 11:36
Copy link
Collaborator

@bgilbert bgilbert left a comment

Choose a reason for hiding this comment

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

A bunch of small notes, but looks good generally. Thanks for your work on this!

license: 'Unlicense',
default_options: [
'c_std=c99',
'default_library=both',
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems surprising and inconsistent as a default. If it's needed because tests require the static library, it seems better to build the library with both_libraries() (or possibly conditionalize on the tests option using build_target()) instead.

Copy link
Collaborator

Choose a reason for hiding this comment

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

...in which case, you should probably also do something like this:

# default_both_libraries requires Meson >= 1.6
if get_option('default_library') == 'static'
libarchive = libarchive_both.get_static_lib()
else
libarchive = libarchive_both.get_shared_lib()
endif

Copy link
Author

@cyanogilvie cyanogilvie Nov 30, 2025

Choose a reason for hiding this comment

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

Can we go with the alternative: conditionalize building and running the tests and set default_library=both in the CI? Hard-coding both_libraries internally and then re-implementing the intent of the default_library standard option seems brittle (the above code would defeat a consumer's intent if they explicitly specify default_library=both - handing them a lib rather than the both_libs they would expect). This way the CI works and runs the tests on all platforms and the consumer gets what they expect with the least surprising internal implementation.

  libtommath_for_tests = libtommath
  if cc.get_id() in ['msvc', 'clang-cl']
    if get_option('default_library') == 'shared'
      error('Static library build is required to run tests with MSVC/clang-cl')
    endif
    libtommath_for_tests = libtommath.get_static_lib()
  endif

I would have liked to use libtommath.get_static_lib().found() to more directly test the relevant requirement, but it seems that libraries() doesn't return both_libs if only one is selected (and so the get_*_library() methods are missing, or only the corresponding method exists).

install: true,
include_directories: inc,
version: meson.project_version(),
link_args: cc.get_id() in ['msvc', 'clang-cl'] ? [
Copy link
Collaborator

Choose a reason for hiding this comment

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

Mild preference for building these up beforehand, which is idiomatic, rather than using ternaries here.

Copy link
Author

Choose a reason for hiding this comment

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

I expressed these as a ternaries to make reasoning about the scope of their effect easier - like that it cannot affect anything other than the link_args of this target, whereas if it is coupled through global state by constructing it as a conditionally defined variable earlier it would require searching for other uses of that variable to understand its scope, but I'll rework it through a variable if the ternary clashes with the idiomatic style of this project?


# Build the manual
with open('bn.ind', 'w') as f:
f.write('hello\n')
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's happening here?

Copy link
Author

Choose a reason for hiding this comment

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

I have no idea. It dates back to a commit in 2003 with the message added libtommath-0.17 which I take to mean that this libtommath repo was extracted from some other project that imported libtommath from elsewhere (libtomcrypt?), or that back then libtommath development didn't use VC, so wasn't able to trace the intent.

f.write('hello\n')

subprocess.run([latex, 'bn'], stdout=subprocess.DEVNULL, check=True)
subprocess.run([latex, 'bn'], stdout=subprocess.DEVNULL, check=True)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this intentionally doubled?

Copy link
Author

Choose a reason for hiding this comment

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

Only insofar as it replicates what upstream's makefile does. My best guess is that there is some build stability hysteresis issue that produces different results if the output artifacts exist already

shutil.copy2(input_file, 'bn.tex')

# Create backup with same timestamp
shutil.copy2('bn.tex', 'bn.bak')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems like there's a lot of unnecessary code to move files around. Why not just write the deterministic header directly into bn.tex (or into tempfile.NamedTemporaryFile()), copy input_file into it afterward, and set its timestamp from input_file?

Copy link
Author

@cyanogilvie cyanogilvie Nov 30, 2025

Choose a reason for hiding this comment

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

Re this and the other review comments on this script: this is a python port of upstream's doc build rules:

docs: manual

#LTM user manual
mandvi: bn.tex
    cp bn.tex bn.bak
    touch --reference=bn.tex bn.bak
    (printf "%s" "\def\fixedpdfdate{"; date +'D:%Y%m%d%H%M%S%:z' -d @$$(stat --format=%Y bn.tex) | sed "s/:\([0-9][0-9]\)$$/'\1'}/g") > bn-deterministic.tex
    printf "%s\n" "\pdfinfo{" >> bn-deterministic.tex
    printf "%s\n" "  /CreationDate (\fixedpdfdate)" >> bn-deterministic.tex
    printf "%s\n}\n" "  /ModDate (\fixedpdfdate)" >> bn-deterministic.tex
    cat bn.tex >> bn-deterministic.tex
    mv bn-deterministic.tex bn.tex
    touch --reference=bn.bak bn.tex
    echo "hello" > bn.ind
    latex bn ${silent_stdout}
    latex bn ${silent_stdout}
    makeindex bn
    latex bn ${silent_stdout}

#LTM user manual [pdf]
manual: mandvi
    pdflatex bn >/dev/null
    sed -b -i 's,^/ID \[.*\]$$,/ID [<0> <0>],g' bn.pdf
    mv bn.bak bn.tex
    rm -f bn.aux bn.dvi bn.log bn.idx bn.lof bn.out bn.toc

Which seem to contain a lot of voodoo in pursuit of deterministic builds, but I honestly don't understand the intent of each bit well enough to reason about which side effects are being relied upon (and the commit history didn't shed any light on it), so I instead aimed to replicate the process and side-effects as exactly as possible.

My python is pretty rusty (I last did any substantial work in python over 5 years ago, and even then not much), so I used AI for the initial port and then checked it as best I can, which probably hasn't helped matters.

For my use case I don't care about deterministic builds (especially for an optional documentation target). That seems like a core focus of other build systems like bazel, and I've seen it mentioned for meson but I don't know what the official stance is towards deterministic builds here. It's curious that libtommath seemed to work so hard on making this output deterministic 23 years go, long before I'd heard anything about deterministic builds.

'bn.lof', 'bn.out', 'bn.toc', 'bn.ilg',
'bn.ind', 'bn.tex'
]
for f in cleanup_files:
Copy link
Collaborator

Choose a reason for hiding this comment

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

These are all written to the build directory. Is it important to clean them up?

Copy link
Author

Choose a reason for hiding this comment

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

I would certainly leave that to the normal build dir handling, but since upstream's rules explicitly cleaned up these files separately from its clean target (which includes a superset of them), I interpreted it as meaning that their presence during subsequent builds would affect the determinism so I cargo culted it.

Copy link
Author

@cyanogilvie cyanogilvie left a comment

Choose a reason for hiding this comment

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

Applied the suggested changes or clarified some reasoning. Some issues still open

install: true,
include_directories: inc,
version: meson.project_version(),
link_args: cc.get_id() in ['msvc', 'clang-cl'] ? [
Copy link
Author

Choose a reason for hiding this comment

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

I expressed these as a ternaries to make reasoning about the scope of their effect easier - like that it cannot affect anything other than the link_args of this target, whereas if it is coupled through global state by constructing it as a conditionally defined variable earlier it would require searching for other uses of that variable to understand its scope, but I'll rework it through a variable if the ternary clashes with the idiomatic style of this project?

shutil.copy2(input_file, 'bn.tex')

# Create backup with same timestamp
shutil.copy2('bn.tex', 'bn.bak')
Copy link
Author

@cyanogilvie cyanogilvie Nov 30, 2025

Choose a reason for hiding this comment

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

Re this and the other review comments on this script: this is a python port of upstream's doc build rules:

docs: manual

#LTM user manual
mandvi: bn.tex
    cp bn.tex bn.bak
    touch --reference=bn.tex bn.bak
    (printf "%s" "\def\fixedpdfdate{"; date +'D:%Y%m%d%H%M%S%:z' -d @$$(stat --format=%Y bn.tex) | sed "s/:\([0-9][0-9]\)$$/'\1'}/g") > bn-deterministic.tex
    printf "%s\n" "\pdfinfo{" >> bn-deterministic.tex
    printf "%s\n" "  /CreationDate (\fixedpdfdate)" >> bn-deterministic.tex
    printf "%s\n}\n" "  /ModDate (\fixedpdfdate)" >> bn-deterministic.tex
    cat bn.tex >> bn-deterministic.tex
    mv bn-deterministic.tex bn.tex
    touch --reference=bn.bak bn.tex
    echo "hello" > bn.ind
    latex bn ${silent_stdout}
    latex bn ${silent_stdout}
    makeindex bn
    latex bn ${silent_stdout}

#LTM user manual [pdf]
manual: mandvi
    pdflatex bn >/dev/null
    sed -b -i 's,^/ID \[.*\]$$,/ID [<0> <0>],g' bn.pdf
    mv bn.bak bn.tex
    rm -f bn.aux bn.dvi bn.log bn.idx bn.lof bn.out bn.toc

Which seem to contain a lot of voodoo in pursuit of deterministic builds, but I honestly don't understand the intent of each bit well enough to reason about which side effects are being relied upon (and the commit history didn't shed any light on it), so I instead aimed to replicate the process and side-effects as exactly as possible.

My python is pretty rusty (I last did any substantial work in python over 5 years ago, and even then not much), so I used AI for the initial port and then checked it as best I can, which probably hasn't helped matters.

For my use case I don't care about deterministic builds (especially for an optional documentation target). That seems like a core focus of other build systems like bazel, and I've seen it mentioned for meson but I don't know what the official stance is towards deterministic builds here. It's curious that libtommath seemed to work so hard on making this output deterministic 23 years go, long before I'd heard anything about deterministic builds.


# Build the manual
with open('bn.ind', 'w') as f:
f.write('hello\n')
Copy link
Author

Choose a reason for hiding this comment

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

I have no idea. It dates back to a commit in 2003 with the message added libtommath-0.17 which I take to mean that this libtommath repo was extracted from some other project that imported libtommath from elsewhere (libtomcrypt?), or that back then libtommath development didn't use VC, so wasn't able to trace the intent.

f.write('hello\n')

subprocess.run([latex, 'bn'], stdout=subprocess.DEVNULL, check=True)
subprocess.run([latex, 'bn'], stdout=subprocess.DEVNULL, check=True)
Copy link
Author

Choose a reason for hiding this comment

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

Only insofar as it replicates what upstream's makefile does. My best guess is that there is some build stability hysteresis issue that produces different results if the output artifacts exist already

'bn.lof', 'bn.out', 'bn.toc', 'bn.ilg',
'bn.ind', 'bn.tex'
]
for f in cleanup_files:
Copy link
Author

Choose a reason for hiding this comment

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

I would certainly leave that to the normal build dir handling, but since upstream's rules explicitly cleaned up these files separately from its clean target (which includes a superset of them), I interpreted it as meaning that their presence during subsequent builds would affect the determinism so I cargo culted it.

@cyanogilvie cyanogilvie requested a review from bgilbert December 2, 2025 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants