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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement multi-package composite dars #18000
base: main
Are you sure you want to change the base?
Conversation
extractNameAndPackageIdFromPath :: FilePath -> (T.Text, LF.PackageId) | ||
extractNameAndPackageIdFromPath = bimap (T.pack . intercalate "-") (LF.PackageId . T.pack) . fromJust . unsnoc . wordsBy (=='-') . takeBaseName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Reconsider this safety...
The PackageId can be derived from the ArchivePayload
The unit-id requires the entire package to be read through proto, so we can read the metadata. Not ideal, perhaps theres some way to lazily parse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been looking into this for longer than I wanted but couldn't get anywhere; the main blocker is that proto3-suite
/proto3-wire
are strict by default because it reads in a Parser
monad so later results depend on earlier ones.
One thing we could do is make a stripped down version of the proto files with just ArchivePayload
, Package
, PackageMetadata
and the interned strings, plus the attending haskell files for that, and use that for this purpose, but that sounds like a lot of copying and pasting.
We could also take the daml2/3 break to move the metadata up into an intermediate level, I don't think we've made any promises for the daml3 encoded dars yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IThe proto format of LF2 is definitely not set in stone, but I'd double-check with @remyhaemmerle-da whether that's worth it because it would be our first truly breaking change.
|
||
let lfVersion = | ||
case nubOrd $ LF.versionMajor <$> darLfVersions of | ||
-- Note that this will not select dev. We may want to detect if any of the packages are `X.dev` and use this if so (and warn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm I think here we should just treat X.dev as the upper bound, not as a special case. If any of the included dars are on LF X.dev, that must have been an intentional choice in a nearby daml.yaml
, or someone is trying to make a composite dar with an X.dev .dalf
that doesn't otherwise interact with the rest, in which case canton will let them know.
From another point of view, other teams will soon start building projects on 2.dev, so this would prevent them from packaging those projects
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that .dev should work and be taken as the max - esp. for the dogfooding reasons Moises mentioned.
conf = mkConfFile name (Just version) [] Nothing [] pkgId | ||
-- Dalfs included unique by packageId | ||
dalfs = nubOrdOn thd3 $ concatMap dalfsToList darDalfs | ||
dar = createArchive name (Just version) unresolvedBuiltinSdkVersion pkgId dalf dalfs "." [] [conf] [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is using unresolvedBuiltinSdkVersion
reasonable here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooh tough one but I think that's the best we can do, I don't think it would make sense to take e.g. the max of each included dar's sdk because that wouldn't play well with 0.0.0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, but that said - we have custom ordering on our versions such that 0.0.0 is maximum, so that would work. But I do think it's misleading to say a dar was "created" with an sdk version it wasn't. We don't maintain sdk versions of dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have custom ordering on our versions such that 0.0.0 is maximum
oh I didn't know/remember this, thanks!
But I do think it's misleading to say a dar was "created" with an sdk version it wasn't
yes exactly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree - the unresolved version is sufficient here. We don't need to resolve versions to order them as greatest.
f704435
to
5b7c362
Compare
Add path and dars fields to composite-dar
961b4bc
to
133cefd
Compare
run-all-tests: true
In a separate PR (after a little discussion), we will add glob support to all |
pkg = LF.Package { LF.packageLfVersion = lfVersion, LF.packageModules = NM.empty, LF.packageMetadata = Just pkgMeta } | ||
(dalf, pkgId) = encodeArchiveAndHash pkg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when I started reviewing this PR I was very curious what would be the main dalf of a composite .dar
; it turned out to be a package without any modules. I wonder if anything on daml or canton relies on there being at least one module in a dalf, but I guess we'll find out :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also a bit anxious about this - atm all composite DAR functionality is only in multi-package.yaml, so we can expect the only people who use it will know it's experimental.
extractNameAndPackageIdFromPath :: FilePath -> (T.Text, LF.PackageId) | ||
extractNameAndPackageIdFromPath = second LF.PackageId . T.breakOnEnd "-" . T.pack . takeBaseName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry I forgot to mention, fst . T.breakOnEnd "-"
will keep a trailing hyphen so we need something like this
extractNameAndPackageIdFromPath :: FilePath -> (T.Text, LF.PackageId) | |
extractNameAndPackageIdFromPath = second LF.PackageId . T.breakOnEnd "-" . T.pack . takeBaseName | |
extractNameAndPackageIdFromPath :: FilePath -> Maybe (T.Text, LF.PackageId) | |
extractNameAndPackageIdFromPath = bimapM (T.stripSuffix "-") (pure . LF.PackageId) . T.breakOnEnd "-" . T.pack . takeBaseName |
One benefit here is that it forces us to handle the Nothing
case, where the basename breaks our assumptions and doesn't actually contain a hyphen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh i saw this in the docs and just completely forgot when implementing, cheers.
run-all-tests: true
, test "Building a transitive composite dar gives an error including where the dar is" ["--composite-dar=unshadowed-transitive"] "./first" compositeDarShadowingProject | ||
$ Left "Found definition for unshadowed-transitive in the following sub-projects" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should this fail if there's a single definition of unshadowed-transitive
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for getting this through @samuel-williams-da!
Just one remaining question about the test case Building a transitive composite dar gives an error including where the dar is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great to me too, thank you!
compositeDarObjects <- fromMaybe [] <$> queryMultiPackageConfig ["composite-dars"] multiPackage | ||
mpCompositeDars <- traverse parseCompositeDar compositeDarObjects | ||
-- n^2 but this list is usually so small that its not worth complicating the logic | ||
forM_ mpCompositeDars $ \cd -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth spinning this into its own utility function "findDuplicates", since we do this pretty often throughout the codebase I find. We can save it for gardening.
<$> (MultiPackageConfigFields <$> traverse canonicalizePath packagePaths) | ||
<$> | ||
( MultiPackageConfigFields | ||
<$> traverse canonicalizePath (mpPackagePaths mpc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth getting rid of canonicalizePath here for something without IO, again potential gardening
[] -> do | ||
hPutStrLn stderr $ "Couldn't find composite dar with the name " <> T.unpack (LF.unPackageName darName) <> " in " <> mpPath multiPackageConfig | ||
warnTransitiveCompositeDar stderr multiPackageConfig darName | ||
exitFailure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we exit with failure after forM
is done running, s.t. an error/warning is printed for every composite dar asked for? This maximizes the amount of useful info for a call.
then toNormalizedFilePath' <$> mpPackagePaths multiPackageConfig | ||
else nubOrd $ concatMap (fmap toNormalizedFilePath' . cdPackages) compositeDars | ||
void $ uses_ BuildMulti packagePaths | ||
liftIO $ traverse_ (buildAndWriteCompositeDar loggerH buildableDataDeps) compositeDars |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This call and its behaviour feels like something that could live in Shake, but this PR is big already so best to put into another PR
pkg = LF.Package { LF.packageLfVersion = lfVersion, LF.packageModules = NM.empty, LF.packageMetadata = Just pkgMeta } | ||
(dalf, pkgId) = encodeArchiveAndHash pkg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also a bit anxious about this - atm all composite DAR functionality is only in multi-package.yaml, so we can expect the only people who use it will know it's experimental.
conf = mkConfFile name (Just version) [] Nothing [] pkgId | ||
-- Dalfs included unique by packageId | ||
dalfs = nubOrdOn thd3 $ concatMap dalfsToList darDalfs | ||
dar = createArchive name (Just version) unresolvedBuiltinSdkVersion pkgId dalf dalfs "." [] [conf] [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree - the unresolved version is sufficient here. We don't need to resolve versions to order them as greatest.
|
||
let lfVersion = | ||
case nubOrd $ LF.versionMajor <$> darLfVersions of | ||
-- Note that this will not select dev. We may want to detect if any of the packages are `X.dev` and use this if so (and warn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that .dev should work and be taken as the max - esp. for the dogfooding reasons Moises mentioned.
#18000! 馃帀
Implements
composite-dars
(Often unofficially called fat-dars)This is an easy way to bunch together a set of Dars into a single dar, without needing an extra daml.yaml and source files that import every module.
It takes the form of an extension to
multi-package.yaml
and a flag.multi-package.yaml
To build this dar, we add the
--composite-dar=NAME
flag todaml build
daml build --composite-dar main-production
This will build the composite dar itself, and any packages it depends on.
We also add
--all-composite-dars
which does as you'd expect.Notes:
--all
does not build composite dars, but can be used with thecomposite-dar
flag to give the union of their behavioursmulti-package.yaml
, sub-projects cannot have their composite dars built without explicitly setting--multi-package-path
to point to them. The CLI will warn you if you attempt to do this, or if you shadow composite-dar names in sub-projects. This behaviour is to avoid awkward shadowing and requiring another flag to specify which composite-dar you want when they collide.packages:
andprojects:
fields inmulti-package.yaml
are optional, the composite-dar feature can be used without using multi-package build behaviour, by only specifyingcomposite-dars
in themulti-package.yaml
, and only using thedars
list within each dar.--composite-dar
flag can be specified multiple times to build many composite-dars in onedaml build
invocation.Tested locally that codegen still works on the generated dars.
TODO: