GithubHelp home page GithubHelp logo

racket2nix's Introduction

Fractalide

Reusable Reproducible Composable Software

LICENSE Build Status

Welcome

What is this?

Fractalide is a free and open source service programming platform using dataflow graphs. Graph nodes represent computations, while graph edges represent typed data (may also describe tensors) communicated between them. This flexible architecture can be applied to many different computation problems, initially the focus will be Microservices to be expanded out into the Internet of Things.

Fractalide is in the same vein as the NSA’s Niagrafiles (now known as Apache-NiFi) or Google’s TensorFlow but stripped of all Java, Python and GUI bloat. Fractalide faces big corporate players like Ab Initio, a company that charges a lot of money for dataflow solutions.

Truly reusable and reproducible efficient nodes is what differentiates Fractalide from the others. It’s this feature that allows open communities to mix and match nodes quickly and easily.

Features

Fractalide stands on the shoulders of giants by combining the strengths of each language into one programming model.

Op Technology Safe Zero-cost Abstractions Reuse Reproducible Distributed Type System Concurrent Service Config Man.

NixOS

+

Nix Expr

+

Rust

+

Flow-based Programming

+

Cap’n Proto

=

Fractalide Model

What’s new from different perspectives

Nix Programmers

Fractalide brings safe, fast, reusable, black-box dataflow functions and a means to compose them.

Tagline: "Nixpkgs is not enough! Here, have 'Nixfuncs' too!"

Rust Programmers

Fractalide brings reproducible, reusable, black-box dataflow functions, a means to compose them and a congruent model of configuration management.

Tagline: Safety extended beyond the application boundary into infrastructure.

Flow-based Programmers

Fractalide brings safe fast reproducible classical Flow-based programming components, and a congruent model of configuration management.

Tagline: Reproducible components!

Programmers

Fractalide brings safe, fast, reusable, reproducible, black-box dataflow functions, a means to compose them and a congruent model of configuration management.

Tagline: Here, have a beer!

Solved problems

Modules-code coupling

Language level modules become tightly coupled with the rest of the code, moving around these modules also poses a problem.

Solution

An unanticipated outcome occurred when combining FBP and Nix. It’s become our peanut butter and jam combination, so to say, but requires a bit of explaining, so hang tight.

Reproducibility

Nix is a content addressable store, so is git, so is docker, except that docker’s SHA resolution is at container level and git’s SHA resolution is at changeset level. Nix on the other hand has a SHA resolution at package level, and it’s known as a derivation. If you’re trying to create reproducible systems this is the correct resolution. Too big and you’re copying around large container sized images with multiple versions occupying gigabytes of space, too small and you run into problems of git not being able to scale to support thousands of binaries that build an operating system. Therefore Nix subsumes Docker.

Indeed it’s these simple derivations that allow python 2.7 and 3.0 to exist side-by-side without conflicts. It’s what allows the Nix community to compose an entire operating system, NixOS. These derivations are what makes NixOS a congruent configuration management system, and congruent systems are reproducible systems. They have to be.

Reusability

Flow-based programming in our books has delivered on its promise. In our system FBP components are known as nodes and they are reusable, clean and composable. It’s a very nice way to program computers. Though, we’ve found, the larger the network of nodes, the more overhead required to build, manage, version, package, connect, test and distribute all these moving pieces. This really doesn’t weigh well against FBP’s advantages. Still, there is this beautiful reusable side that is highly advantageous! If only we could take the good parts?

Reproducibility + Reusability

When nix is assigned the responsibility of declaratively building fbp nodes, a magic thing happens. All that manual overhead of having to build, manage and package etc gets done once and only once by the node author, and completely disappears for everyone thereafter. We’re left with the reusable good parts that FBP has to offer. Indeed the greatest overhead a node user has, is typing the node's name. We’ve gone further and distilled the overhead to a few lines, no more intimidating than a typical config file such as Cargo.toml:

{ agent, edges, mods, pkgs }:

agent {
  src = ./.;
  edges = with edges; [ PrimText FsPath ];
  mods = with mods.rs; [ rustfbp rusqlite ];
  osdeps = with pkgs; [ sqlite pkgconfig ];
}

Now just to be absolutely clear of the implications; it’s possible to call an extremely complex community developed hierarchy of potentially 1000+ nodes, where each node might have different https://crates.io dependencies, they might have OS level dependencies such as openssl etc and nix will ensure the entire hierarchy is correctly built and made available. All this is done by just typing the node name and issuing a build command.

It’s this feature that sets us apart from Google TensorFlow and Apache-NiFi. It contains the DNA to build a massive sprawling community of open source programmers, this and the C4, that is. It’s our hope anyway!

Complex configuration management model

The vast majority of system configuration management solutions use either the divergent or convergent model.

We’re going to quote Steve Traugott’s excellent work verbatim.

Divergent

divergent

"One quick way to tell if a shop is divergent is to ask how changes are made on production hosts, how those same changes are incorporated into the baseline build for new or replacement hosts, and how they are made on hosts that were down at the time the change was first deployed. If you get different answers, then the shop is likely divergent.

The symptoms of divergence include unpredictable host behavior, unscheduled downtime, unexpected package and patch installation failure, unclosed security vulnerabilities, significant time spent "firefighting", and high troubleshooting and maintenance costs."

— Steve Traugott

Convergent

convergent

"The baseline description in a converging infrastructure is characteristically an incomplete description of machine state. You can quickly detect convergence in a shop by asking how many files are currently under management control. If an approximate answer is readily available and is on the order of a few hundred files or less, then the shop is likely converging legacy machines on a file-by-file basis.

A convergence tool is an excellent means of bringing some semblance of order to a chaotic infrastructure. Convergent tools typically work by sampling a small subset of the disk - via a checksum of one or more files, for example - and taking some action in response to what they find. The samples and actions are often defined in a declarative or descriptive language that is optimized for this use. This emulates and preempts the firefighting behavior of a reactive human systems administrator - "see a problem, fix it." Automating this process provides great economies of scale and speed over doing the same thing manually.

Because convergence typically includes an intentional process of managing a specific subset of files, there will always be unmanaged files on each host. Whether current differences between unmanaged files will have an impact on future changes is undecidable, because at any point in time we do not know the entire set of future changes, or what files they will depend on.

It appears that a central problem with convergent administration of an initially divergent infrastructure is that there is no documentation or knowledge as to when convergence is complete. One must treat the whole infrastructure as if the convergence is incomplete, whether it is or not. So without more information, an attempt to converge formerly divergent hosts to an ideal configuration is a never-ending process. By contrast, an infrastructure based upon first loading a known baseline configuration on all hosts, and limited to purely orthogonal and non-interacting sets of changes, implements congruence. Unfortunately, this is not the way most shops use convergent tools…​"

— Steve Traugott

Solution

Congruent
congruent

"By definition, divergence from baseline disk state in a congruent environment is symptomatic of a failure of code, administrative procedures, or security. In any of these three cases, we may not be able to assume that we know exactly which disk content was damaged. It is usually safe to handle all three cases as a security breach: correct the root cause, then rebuild.

You can detect congruence in a shop by asking how the oldest, most complex machine in the infrastructure would be rebuilt if destroyed. If years of sysadmin work can be replayed in an hour, unattended, without resorting to backups, and only user data need be restored from tape, then host management is likely congruent.

Rebuilds in a congruent infrastructure are completely unattended and generally faster than in any other; anywhere from ten minutes for a simple workstation to two hours for a node in a complex high-availability server cluster (most of that two hours is spent in blocking sleeps while meeting barrier conditions with other nodes).

Symptoms of a congruent infrastructure include rapid, predictable, "fire-and-forget" deployments and changes. Disaster recovery and production sites can be easily maintained or rebuilt on demand in a bit-for-bit identical state. Changes are not tested for the first time in production, and there are no unforeseen differences between hosts. Unscheduled production downtime is reduced to that caused by hardware and application problems; firefighting activities drop considerably. Old and new hosts are equally predictable and maintainable, and there are fewer host classes to maintain. There are no ad-hoc or manual changes. We have found that congruence makes cost of ownership much lower, and reliability much higher, than any other method."

— Steve Traugott

Fractalide does not violate the congruent model of Nix, and it’s why NixOS is a dependency. Appreciation for safety has extended beyond the application boundary into infrastructure as a whole.

Language choice

A language needed to be chosen to implement Fractalide. Now as Fractalide is primarily a Flow-based programming environment, it would be beneficial to choose a language that at least gets concurrency right.

Solution

Rust was a perfect fit. The concept of ownership is critical in Flow-based Programming. The Flow-based scheduler is typically responsible for tracking every Information Packet (IP) as it flows through the system. Fortunately Rust excels at getting the concept of ownership right. To the point of leveraging this concept that a garbage collector is not needed. Indeed, different forms of concurrency can be layered on Rust’s ownership concept. One very neat advantage Rust gives us is that we can very elegantly implement Flow-based Programming’s idea of concurrency. This makes our scheduler extremely lightweight as it doesn’t need to track IPs at all. Once an IP isn’t owned by any component, Rust makes it wink out of existence, no harm to anyone.

API contracts

It’s easy to disrespect API contracts in a distributed services setup.

Solution

We wanted to ensure there was no ambiguity about the shape of the data a node receives. Also if the shape of data changes, the error must be caught at compile time. Cap’n Proto schema fits these requirements, and fits them perfectly when nix builds the nodes calling the Cap’n Proto schema. Because, if a schema changes, nix will register the change and will rebuild everything (nodes and subgraphs) that depends on that schema, thus catching the error. We’ve also made it such, during graph load time agents cannot connect their ports unless they use the same Cap’n Proto schema. This is a very nice safety property.

The mandatory Hello-like World example.

From a fresh install of NixOS (using the nixos-unstable channel) we’ll build the fractalide virtual machine (fvm) and execute the humble NAND logic gate on it.

$ git clone https://github.com/fractalide/fractalide.git
$ cd fractalide
$ nix-build --argstr node test_nand
...
$ ./result
boolean : false

Contributing to Fractalide

Contributing FAQ

Q: I’m kind of new to Github, how do I get started?

  • Read the C4.2 (Collective Code Construction Contract) and the line by line explanation of the protocol.

  • Fork this github repository under your own github account.

  • Clone your fork locally on your development machine.

  • Choose one problem to solve. If you aren’t solving a problem that’s already in the issue tracker you should describe the problem there (and your idea of the solution) first to see if anyone else has something to say about it (maybe someone is already working on a solution, or maybe you’re doing something wrong). If the issue is in the issue tracker, you should comment on the issue to say you’re working on the solution so that other people don’t work on the same thing.

  • Add the Fractalide repository as an upstream source and pull any changes:

$ git remote add upstream git://github.com/fractalide/fractalide //only needs to be done once
$ git checkout master //just to make sure you're on the correct branch
$ git pull upstream master //this grabs any code that has changed, you want to be working on the latest 'version'
$ git push //update your remote fork with the changes you just pulled from upstream master
  • Create a local branch on your machine git checkout -b branch_name(it’s usually a good idea to call the branch something that describes the problem you are solving).

  • Solve the problem in the absolute most simple and fastest possible way with the smallest number of changes humanly possible. Tell other people what you’re doing by putting very clear and descriptive comments in your code every 2-3 lines. Add your name to the AUTHORS file so that you become a part owner of Fractalide.

  • Commit your changes to your own fork: Before you commit changes, you should check if you are working on the latest version (again). Go to the github website and open your fork of Fractalide, it should say This branch is even with Fractalide:master. If not, you need to pull the latest changes from the upstream Fractalide repository and replay your changes on top of the latest version:

$ git stash //save your work locally
$ git checkout master
$ git pull upstream master
$ git push
$ git checkout -b branch_name_stash
$ git stash pop //_replay_ your work on the new branch which is now fully up to date with the fractalide repository

Now you can add and commit your changes:

$ git add changed_file.js //repeat for each file you changed
$ git commit -m 'problem: very short description of problem //do not close the '', press ENTER two (2) times
>
>solution: short description of how you solved the problem.' //Now you can close the ''. Also mention the issue number if there is one (e.g. #6)
$ git push //this will send your changes to _your_ fork on Github
  • Go to your fork on Github and select the branch you just worked on. Click "pull request" to send a pull request back to the Fractalide repository.

  • Send the pull request.

Q: What happens after I send a pull request?

If your pull request contains a correct patch (read the C4) a maintainer should merge it. If you want to work on another problem in the meantime simply repeat the above steps starting at:

$ git checkout master

Q: Can I be paid to contribute to Fractalide?

Yes, this is sometimes possible. Your first step is to very carefully read and understand everything above, including the linked files, then start fixing problems and sending pull requests! If your code is amazing and brilliant but you don’t understand the contribution process we cannot consider you for a paid position. Make sure you follow the project on Github so you get updates. Contact Fractalide’s BDFL (Benevolent Dictator For Life): Stewart Mackenzie if you’ve been contributing code to Fractalide and want to keep doing it but you require financial assistance.

Consulting and Support

Name Info Language

Stewart Mackenzie

Founder and maintainer of Fractalide

English

Denis Michiels

Founder and maintainer of Fractalide

French

License

The project license is specified in LICENSE. Fractalide is free software; you can redistribute it and/or modify it under the terms of the Mozilla Public License Version 2 as approved by the Free Software Foundation.

Social

Follow us on twitter

Thanks

  • Peter Van Roy

  • Pieter Hintjens

  • Joachim Schiele & Paul Seitz

  • P Meunier

racket2nix's People

Contributors

clacke avatar mfelleisen avatar sjmackenzie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

racket2nix's Issues

Problem: We're building an empty drracket package

$ tree /nix/store/yb6ajqx12ygdg95azxrr4r3q516rj0xf-drracket
/nix/store/yb6ajqx12ygdg95azxrr4r3q516rj0xf-drracket
|-- etc
|   `-- racket
|       `-- config.rktd
`-- share
    `-- racket

4 directories, 1 file

Problem: Figuring out dependencies requires network access

This may be unavoidable in some cases, but it would be nice if it were at least possible to feed racket2nix a catalog and let it operate off that. Currently we use get-pkg-details-from-catalogs and that goes straight to download.racket-lang.org.

This makes operations slow, and it also affects testability. Also it would be nice to be able to run racket2nix in a nix build.

Problem: Our progress feedback is spammy

An improvement would be e.g. a nice single line, updating in-place, with a Packages evaluated: [0/1] updating both numbers as we discover more dependencies and evaluate (examine? generate?) them.

Problem: no neat way for downstream users to refer to catalog.rktd

In fractalide/cardano-wallet I'm now using this horrible thing to use racket2nix in pretty much the most useful way:

racket2nix_src_ref=$(nix-build --no-out-link -E 'with import <nixpkgs> {}; runCommand "racket2nix-src" { src = fetchTarball { url = https://github.com/fractalide/racket2nix/archive/736a25bc690fa67fc86665f6f6e2153d259f4b62.tar.gz; sha256 = "18dcmwgyh0lclin0xg80h3cnp4d6jxmw6dllfzcma0x91pb7k96s"; }; } "echo $src > $out"')
racket2nix=$(cat $racket2nix_src_ref)

nix-shell --pure -p racket-minimal nix-prefetch-git --run "$racket2nix/racket2nix --catalog catalog.rktd.in --catalog $racket2nix/catalog.rktd --export-catalog fractalide | racket -e '(pretty-write (read))' > catalog.rktd"

Solution: Our catalog.rktd should be in the collection. racket2nix should use it by default, in addition to any catalogs the users provide.

Possible set of options:

  • --no-base-catalog for using only what comes in through --catalog
  • --system-base-catalog for using (get-all-pkg-details-from-catalogs) (today's default)

Problem: generated derivations cause filename collisions

Each of our generated derivations has a etc/config/racket.rktd and a bin/racket. These should probably be put elsewhere.

The documentation near (find-addon-tethered-console-bin-dir) is probably part of the solution, as it might help us generate launchers that point to a customized racket like the wrapper we are using now, but one generated by raco setup itself.

It also serves as a reminder that we shouldn't conflate the uses of etc/config/racket.rktd and share/racket/etc/config.rktd.

Problem: we cannot handle versioned dependencies

Example:

              (dependencies
               .
               ("base"
                "draw-lib"
                "drracket"
                ("gui-lib" #:version "1.16")

Currently we won't even be able to do an incorrect parsing of this, I'm guessing we'll get a type error for expecting a string when there's a pair.

Solution: Maybe short-term we could try getting away with just ignoring the version, just like we are ignoring the platform of platform-dependent packages in the release catalog.

Long-term we'll have to rework things to allow for our nix expression to address several versions of the same package.

Problem: impure derivations not tolerated

It seems that Nix 2.0.1 or 2.0.2 fixed the hole we noticed in fractalide/fractalide#129 .

I now get this:

$ nix-build -A pkgs.fractalide
building '/nix/store/j548h1sl2g7v25nlfm3ci4307cnixi3g-racket-package.nix.drv'...
installing
Initialized empty Git repository in /tmp/nix-build-racket-package.nix.drv-0/git-checkout-tmp-nKXi8SHa/typed-map-c9c5a23/.git/
From git://github.com/jsmaniac/typed-map
 * branch            HEAD       -> FETCH_HEAD
Switched to a new branch 'fetchgit'
removing `.git'...
error: cannot open connection to remote store 'daemon': reading from file: Connection reset by peer
builder for '/nix/store/j548h1sl2g7v25nlfm3ci4307cnixi3g-racket-package.nix.drv' failed with exit code 1
error: build of '/nix/store/j548h1sl2g7v25nlfm3ci4307cnixi3g-racket-package.nix.drv' failed

Solution: Instead of generating the catalog in a derivation, generate it offline, and as we generate it, add Nix sha256s for every git repo (as the checksum there refers to git rev, not checksum of the tree). Adapt racket2nix to making fixed derivations for git sources.

Problem: launchers aren't getting created

It's not visible with the racket-bundled pkgs, because they don't define any launchers (side note: why isn't drracket defined as a launcher in the drracket pkg?) but racket2nix has a defined launcher, yet no launcher is created in $out/bin/racket2nix, even though one is created when you do a simple raco pkg install of it in the normal racket world.

Problem: failure to handle directory name ending in slash

It's pretty expected that the user would want to do racket2nix ../mypac<tab>, but then the autocompletion would put a slash at the end, and we would name the package the empty string, which doesn't work with nix.

Solution: Strip any trailing slashes from the package name given on the command line.

Problem: nix expression has a broken bash script

installing
/nix/store/0igxxngigjgfkgg3cw547n92b8bpg6b1-stdenv-darwin/setup: eval: line 1232: syntax error near unexpected token `('
builder for '/nix/store/09cpbqj55lzfly3q06360qm1j5wfykrb-base.drv' failed with exit code 2

Problem: No racket interface

This should be a library with a thin command-line wrapper, to that racket code can call our library code in-process.

Problem: We are not rebuilding 'built dependencies from source

When we install dependencies they may come with compiled bytecode and we won't rebuild it on raco setup.

If we run raco setup --clean it will try to remove too much and fail when it tries to remove files from other derivations.

Probable solution: Just remove all the compiled directories.

Problem: raco setup wants to compile files that are already compiled

This is most of the cause behind #38. Currently racket-doc cannot run raco setup, because it wants to recompile everything it touches.

This may be because it considers the compiled files not-newer than the source files. Maybe we need to patch setup or the compiler to accept equally-old files.

Or it may be because setup cannot find compiled files that aren't in the primary installation scope. This may be the same thing that made us want to symlink racket/share/racket/collects into the primary installation scope while setting up.

Problem: Not parsing real info.rkt files

Currently the "info.rkt" we are testing against is just one line with a package name. It should be a real info.rkt, and we should be able to generate nix for a package that is not in the racket catalogs, not just those that can be found by name using get-pkg-details-from-catalogs.

Problem: We are not running raco setup

As we are not running raco setup at the moment, we are probably just using the .zos that come in with the package. We should be reproducible and transparent and build everything from source.

Problem: we are fetching git repos over and over

Every time you run racket2nix with some git dependencies, it will nix-prefetch-git those dependencies to figure out their checksum. This could easily involve racket, which with not-shallow cloning weighs in at ~170 MB.

Solution: A central mapping from URL,rev to sha256 is probably overkill and even a bad idea, but a local cache sounds like a good idea.

If there is a reasonable/portable/configurable way, put it in ~/.cache somewhere, otherwise use the racket user dir.

Problem: full-racket detection fails for drracket

If you build drracket with racket, not racket-minimal, you get:

package is currently installed in a wider scope

The reason the simplistic one-liner can't detect this condition is probably the same reason it ultimately builds an empty package against racket-minimal. We can successfully install pre-packaged simple packages, but no thought has yet gone into making e.g. multi-collection packages.

Problem: We aren't handling man pages correctly

In contrast to #7, this one happens only with full racket, and not with racket-minimal:

$ ./racket2nix <<< 'gui-lib' && nix-build
[ . . . ]
raco setup: --- updating info-domain tables ---
raco setup: updating: /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/info-cache.rktd
raco setup: WARNING: with-output-to-file: error deleting file
  path: /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/info-cache.rktd
  system error: Permission denied; errno=13
[ . . . ]
raco setup: --- installing man pages ---
raco setup: installing: man page /nix/store/kvy5ls213c2z7hzw2xw346asqja6fq1j-class-iop-lib/share/racket/6.12/man/man1/racket.1
raco setup: installing: man page <man>/man1/gracket.1
copy-file: cannot open destination file
  source path: /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/pkgs/gui-lib/racket/gui/gracket.1
  destination path: /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/man/man1/gracket.1
  system error: Permission denied; errno=13
  context...:
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/setup/setup-core.rkt:1747:30: copy-lib
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/setup/setup-core.rkt:1775:30: for-loop
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/setup/setup-core.rkt:178:50
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/setup/setup-core.rkt:1724:6: for-loop
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/setup/setup-core.rkt:1720:4: make-libs-step
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/setup/setup-core.rkt:71:0: setup-core
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/setup/setup.rkt:65:3
   /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/pkg/main.rkt:17:0: setup
   (submod /nix/store/hmdy6zq34vq0761l4lym9cl3piw9b6gc-racket-6.12/share/racket/collects/pkg/main.rkt main): [running body]
[ . . . ]

These files just don't exist in racket-minimal, so that's why this doesn't happen there. But there are several things worth looking into here:

  • Which dependency is it installing here that makes it bring manpages along?
  • This looks like it's happening pretty early, so it should be racket-lib or base, but:
    • Why don't they have manpages when we build them for racket-minimal?
      Alternatively:
    • Why does class-iop-lib want to copy their manpages over when other packages don't?
  • We don't want to be copying these files at all.
  • If we did, we wouldn't want to copy them right back to where they came from, we'd want the copied to our $out.

Bonus question: What's share/racket/info-cache.rktd for? There's some package info in there, and it's for certain that we aren't generating one for the nix packages we are generating.

Problem: we cannot handle catalog entries using 'versions

Here is an example of a package in http://pkgs.racket-lang.org/pkgs-all that uses the 'versions attribute:

       ("games"
        .
        #hash((build
[ . . . ]
              (versions
               .
               #hash(("5.3.4"
                      .
                      #hash((source_url
                             .
                             "http://racket-packages.s3-us-west-2.amazonaws.com/pkgs/empty.zip")
                            (checksum
                             .
                             "9f098dddde7f217879070816090c1e8e74d49432")
                            (source
                             .
                             "http://racket-packages.s3-us-west-2.amazonaws.com/pkgs/empty.zip")))
[ . . . ]
                     (default
                      .
                      #hasheq((source_url . "git://github.com/racket/games/")
                              (checksum
                               .
                               "7b38168dc28ceb607e66717a4c621bebddce35ec")
                              (source . "git://github.com/racket/games/")))))
[ . . . ]
              (source
               .
               "http://racket-packages.s3-us-west-2.amazonaws.com/pkgs/empty.zip")

Currently we are only reading the 'source attribute, so we are getting empty.zips for several packages. It looks like we will generally be able to assume that the 'default version is good enough for the latest racket, and versioned entries are generally for racket 5.x versions.

Solution: If a package has a 'version with a 'default in it, use 'source_url (or 'source? are they different?) and 'checksum from there, rather than 'source and 'checksum from the root of the entry. Alternatively maybe only do this if the root 'source points at "http://racket-packages.s3-us-west-2.amazonaws.com/pkgs/empty.zip".

Problem: find-relevant-directories does not work across derivations

find-relevant-directories:

Returns a list of paths identifying collections and installed PLaneT packages whose "info.rkt" file defines one or more of the given symbols. The result is based on a cache that is computed by raco setup.

That cache is the share/racket/info-cache.rktd file, and racket looks only in the directory of the current installation scope. Other configuration sources have config.rktd attributes like 'lib-search-dirs or 'links-search-files, but for the info cache and a couple of other things there is only the one 'share-dir.

This means that e.g. raco test won't work unless compiler-lib is built from source together with your topmost package.

Solution: When building the derivation, merge the info-cache.rktds of its racket dependencies.

Problem: We are writing links.rktd to the wrong place

The distribution has its share/racket/links.rktd, but when we are installing dependencies and are installing our current package, it's $out/share/racket/pkgs/links.rktd that gets written to. Probably appropriately so, as we are using --scope-dir $out/share/racket/pkgs.

Can we use --scope-dir $out/share/racket and still end up with the package in .../pkgs? Maybe this would solve other issues as well.

Problem: racket thinks our "pkgs paths" includes $out/share/racket/6.12/pkgs

We want to write our packages to $out/share/racket/pkgs and we are doing so, but we're getting there by using --scope-dir $out/share/racket/pkgs and it's likely we are messing other things up by doing so. We may need to find a way to make racket believe the system racket dir is in $out/share/racket and just use --scope installation. That would probably solve a whole host of things regarding these various paths for things.

Problem: our shallowing of directories is too narrow

https://docs.racket-lang.org/pkg/Package_Concepts.html#%28tech._package._source%29 explicitly says:

An archive represents package content analogous to a directory, but if the archive’s content is contained within a single top-level directory, then the directory’s content (as opposed to the overall archive content) is used as the package content.

So our current shallowing routine, only shallowing a directory with the same name as the unpacked file, is too narrow. We could just go and shallow every source directory that has a single directory in its root. Take care not to miss any hidden files.

Problem: build-racket does not work with restrict-eval = true

$ nix-build -I . -I /nix/store/8wpfckjbdwhrfrfdra6gi2f69brdyz20-nixpkgs-18.09pre140705.090b7cc8f1b/nixpkgs build-racket.nix --arg package ./nix
building '/nix/store/dh56a2qmy6x65pzscxn85dkr74pkbx9v-racket-package.nix.drv'...
installing
error: access to path '/nix/store/x8fbigk4ms45j7dpfcdi97x834mmynrd-racket-package.nix-src/nix' is forbidden in restricted mode

Solution: When a package is located inside the store, racket2nix refers to it as a no-op fixed-output derivation instead of as a plain path.

Problem: We are patching racket-index

Find out how to please it or turn it off instead. Maybe it's as easy as touching a file somewhere.

It expects a documentation index database to be present in the installation scope, which I assume it initializes when it first sets itself up. I don't know if the database can work at all in our multiple-installation-scope setup. If you want it, probably you'll have to merge the databases from our dependencies, and then use that as a starting point when setting up the current packages.

Problem: extractPath doesn't work on Nix/Darwin

$ nix-build -A pkgs.fractalide
error: invalid regular expression '(|.*/)([^/]*)', at /Users/cw/src/git/fractalide/fractalide/pkgs/fractalide.nix:29:27

Works on Nix/Linux. Are they using different regex libraries? Both are Nix 2.0pre.

Problem: fixed-derivation may need a fallback (not be no-op)

~/git/fractalide/cardano-wallet/mods/rkt]$ nix-bu
these derivations will be built:
  /nix/store/qg66wal7jmlryjgb79a4s5144r1gd8q0-fractalide.drv
  /nix/store/gaalgi2bjz3wviwnr2cqy0xc90rr2jyd-fractalide.drv
  /nix/store/x5s241g3fq74i9ha5s4dwpm6qdqfj9x9-fvm.drv
  /nix/store/6cd0nk0b8z8djclcjjk9h7wrjfybbb66-fvm.drv
  /nix/store/ig9q4i024n8702gilpv5i3nzpwivpyc9-cardano-wallet.drv
  /nix/store/a8895mqprgfx618wfj8bp6h8ccsbqgs5-cardano-wallet.drv
building '/nix/store/ig9q4i024n8702gilpv5i3nzpwivpyc9-cardano-wallet.drv'...
building '/nix/store/qg66wal7jmlryjgb79a4s5144r1gd8q0-fractalide.drv'...
building '/nix/store/x5s241g3fq74i9ha5s4dwpm6qdqfj9x9-fvm.drv'...
ERROR: This source should have been put here as part of running racket2nix.
It has been garbage-collected through a confluence of unlikely events.
ERROR: This source should have been put here as part of running racket2nix.
It has been garbage-collected through a confluence of unlikely events.

Maybe not as unlikely as I thought.

Problem: raco pkg install can't handle dangling symlinks

$ ln -s asdfasdfasdf nix/asdf
$ make
copy-directory/files: encountered path that is neither file nor directory                   [18/1069]
  path: ./nix/asdf
  context...:
   /nix/store/pv5fnjv97wprb5p913xrcnqyrbb993m1-racket-minimal-6.12/share/racket/collects/racket/file.
rkt:90:21
   /nix/store/pv5fnjv97wprb5p913xrcnqyrbb993m1-racket-minimal-6.12/share/racket/collects/racket/file.
rkt:76:2: loop
   /nix/store/pv5fnjv97wprb5p913xrcnqyrbb993m1-racket-minimal-6.12/share/racket/collects/pkg/private/
stage.rkt:107:0: stage-package/info46
   /nix/store/pv5fnjv97wprb5p913xrcnqyrbb993m1-racket-minimal-6.12/share/racket/collects/pkg/private/
install.rkt:659:4: for-loop

A concrete, non-contrived example would be the result links that nix-build leaves lying around, and which are dangling when in the build sandbox.

Solution: Remove all dangling symlinks in postUnpack.

Problem: Our handling of circular dependencies is incomplete

This issue is a continuation of PR #40, and by mentioning it here I'm making the two an circular dependency of information. Resolve this by reading either you prefer first, and lazily waiting to parse the other. :-)

Reading #40 first probably makes the most sense.

Next steps: First, do (2) automatically instead of special-cased as
in this commit. When we break a circular dependency, don't just mark
the bottom package with circularBuildInputs, also mark the top package
with reverseCircularBuildInputs. When we build that package, we do
what we now special-cased for htdp-lib: remove the
reverseCircularBuildInputs from your config.rktd dependencies, unpack
them all next to you, set them all up with their dependencies
half-installed in the current installation scope.

Now, this will make the top package privileged, and anything
depending on a lower package that directly referenced the top package
will get a half-installed package as a dependency.

The next-next step might be to break the asymmetry by detecting the
full circle, install all the packages in the circle as one derivation,
and then figure out what to do with that. Maybe let each of the
package names be an alias to that package.

Another idea might be to give each package in the circle the chance
to be the privileged package. In practice this means you would have a
number of derivations that are isomorph, each with the same packages
installed in that scope, but each with its own name and with a
slightly differently shaped dependency tree underneath. Not sure if
this is horrible. It does mean some package depending on two packages
in the circle will result in a derivation that has both the
half-installed and the fully-setup package in its config.rktd, and
you are likely to get some whining from raco about that.

Problem: We aren't handling collections properly

Packaging e.g. at-exp-lib happens to work with full racket, but with racket-minimal it's clear that we're not doing enough about collections:

$ ./racket2nix <<< at-exp-lib > default.nix && nix-build -E 'with import ../../../nix/racket-minimal {}; let racket = racket-minimal; in import ./. { inherit racket; }'
[ . . . ]
raco setup: main collects: /nix/store/i1shb9kq2q5s2jvqxgz1msz68mhz4a60-at-exp-lib/share/racket/collects
raco setup: collects paths: 
raco setup:   /nix/store/i1shb9kq2q5s2jvqxgz1msz68mhz4a60-at-exp-lib/share/racket/collects
raco setup:   /nix/store/rm4jgl2jhdmv0b2jlr2jmziih8clximf-racket-minimal-6.12/share/racket/collects
raco setup: main pkgs: /nix/store/rm4jgl2jhdmv0b2jlr2jmziih8clximf-racket-minimal-6.12/share/racket/pkgs
raco setup: pkgs paths: 
raco setup:   /nix/store/rm4jgl2jhdmv0b2jlr2jmziih8clximf-racket-minimal-6.12/share/racket/pkgs
raco setup:   /nix/store/i1shb9kq2q5s2jvqxgz1msz68mhz4a60-at-exp-lib/share/racket/6.12/pkgs
raco setup: links files: 
raco setup:   /nix/store/rm4jgl2jhdmv0b2jlr2jmziih8clximf-racket-minimal-6.12/share/racket/links.rktd
[ . . . ]
raco setup: --- post-installing collections ---
collection-path: collection not found
  collection: "at-exp"
  in collection directories:
   /nix/store/i1shb9kq2q5s2jvqxgz1msz68mhz4a60-at-exp-lib/share/racket/collects
   /nix/store/rm4jgl2jhdmv0b2jlr2jmziih8clximf-racket-minimal-6.12/share/racket/collects
   /nix/store/rm4jgl2jhdmv0b2jlr2jmziih8clximf-racket-minimal-6.12/share/racket/pkgs/racket-lib
builder for ‘/nix/store/bxjnylw0nihs9i7r5zpgh2aph7s4wzw2-at-exp-lib.drv’ failed with exit code 1
error: build of ‘/nix/store/bxjnylw0nihs9i7r5zpgh2aph7s4wzw2-at-exp-lib.drv’ failed

The astute observer will notice other details wrong with this output, but that's for other issues.

Problem: redundant transitive dependencies

$ printf '%s\n' $(grep -A 9 _drracket.= default.nix | grep buildInputs | sed -e 's/.*[[]//' -e 's/[]].*//')|wc -l                                                                                                           
1588
$ printf '%s\n' $(grep -A 9 _drracket.= default.nix | grep buildInputs | sed -e 's/.*[[]//' -e 's/[]].*//')|sort -u|wc -l                                                                                                   
135

Fix this in the lambda in name->transitive-dependency-names.

Problem: we cannot package hyperflow

This is an umbrella issue. We will probably find several specific issues as we progress.

  • hyperflow depends on typed-map, and that results in us having to handle git references phrased as https, git and github, see #20.

Problem: nix-prefetch-git output is noisy and confusing

The user might not believe that this much output is the result of a successful call to racket2nix.

Solution: Swallow the stderr, maybe providing a brief progress indicator like "Fetching git://blah/...". Only reveal the full stderr if nix-prefetch-git fails.

Initialized empty Git repository in /private/var/folders/4h/667g12ds42s7tnt7rwqyk0hw0000gn/T/git-checkout-tmp-vw4vAApW/typed-map-c9c5a23/.git/
remote: Counting objects: 25, done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 25 (delta 3), reused 14 (delta 0), pack-reused 0
Unpacking objects: 100% (25/25), done.
From git://github.com/jsmaniac/typed-map
 * branch            HEAD       -> FETCH_HEAD
Switched to a new branch 'fetchgit'
removing `.git'...

git revision is c9c5a236f4e32d9391df3edffdf9b1a55401fe31
path is /nix/store/iqwr50i4553bnpmy2a7m7j4ginivap42-typed-map-c9c5a23
git human-readable version is -- none --
Commit date is 2018-01-27 13:25:01 +0100
hash is 150agc51y1kvrarg0n6r2x6n3awnvivqj5k78gx9ngr8q31zl83f

Problem: Not able to package ourselves

racket2nix should itself be a properly defined racket package with a real info.rkt and it should be able to generate a default.nix for itself from the info.rkt. This issue comprises several other issues, written and not yet written.

Problem: missing the ultimate one-liner

We are able to use buildRacket { package = "foo"; } from Nix if we fetch and import racket2nix, and we are able to do nix-build build-racket.nix --argstr package foo from the command-line if we're in a checked-out racket2nix.

But we cannot do nix-build https://github.com/fractalide/racket2nix/archive/f4636fd3b6c19b7d928092ecf2fa207c0a6ae5c2.tar.gz --argstr package foo from an arbitrary place in the cosmos.

Solution: Wrap a package argument in default.nix just like in build-racket.nix, and forward to build-racket.nix if it is non-null.

Other things like merging catalogs should also be possible to do in neat one-liners.

Problem: we are fetching full git repos

A shallow clone of racket is 10 MB, a full clone is 170 MB.

The way I read nix-prefetch-git is that it is supposed to make a shallow clone unless you tell it otherwise, but evidence shows otherwise.

Solution: Call nix-prefetch-git --no-deepClone, and figure out a way to do the same in the nix expression. Is giving it deepClone = false enough, or is that getting overridden somewhere?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.