-
Notifications
You must be signed in to change notification settings - Fork 7
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
Rework how packages describe how they contain the components of an embedded package #744
Conversation
crates/spk-solve/src/solver_test.rs
Outdated
{"name": "comp1"}, | ||
{"name": "comp2"}, |
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.
The solve fails if both the components aren't named here, because when it tries to find a build of victim
it sees this spec and claims real-pkg
doesn't have a component named comp2
("does not define requested components").
Test output here for reference:
|
So I think what is missing is in two parts:
|
Now I'm thinking the problem runs deeper, and the spec schema has a problem that two incompatible specs can be presented for the same embedded package in different components. Example: pkg: embedder/1.0.0
install:
components:
- name: comp1
embedded:
- pkg: embedee/1.0.0
install:
components:
- name: comp1
- name: comp2
embedded:
- pkg: embedee/2.0.0
install:
components:
- name: comp2 The TODOs in the test mention how this causes solver problems, and this goes a step further to show how two different incompatible versions of What piece of information that is needed in the pkg: embedder/1.0.0
install:
components:
- name: comp1
embedded_packages:
- embedee:comp1
- name: comp2
embedded_packages:
- embedee:{comp2,comp3}
embedded:
- pkg: embedee/1.0.0
install:
components:
- name: comp1
- name: comp2
- name: comp3 The embedded package Now it is clear what the spec for the embedded stub should be (it only appears once in the parent spec) and it's clearer that this spec needs to define all the components of the embedded package. |
I checked in some code while I noodle around with ideas. There definitely isn't good test coverage for this territory because all the tests are passing, but this code is almost certainly still broken. |
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## main #744 +/- ##
==========================================
+ Coverage 53.55% 54.50% +0.95%
==========================================
Files 258 254 -4
Lines 20466 20107 -359
==========================================
- Hits 10960 10959 -1
+ Misses 9506 9148 -358 ☔ View full report in Codecov by Sentry. |
From the meeting today:
|
0632e01
to
ddf8caa
Compare
I added support for embedded multiple versions of the same package. It complicated the code by a large factor. |
At this stage I think |
191a250
to
b714d99
Compare
{"name": "build", "embedded_packages": ["dep-e1:all"]}, | ||
{"name": "run", "embedded_packages": ["dep-e2:all"]}, | ||
], | ||
"embedded": [ | ||
{"pkg": "dep-e1/1.0.0"}, | ||
{"pkg": "dep-e2/1.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.
Highlighting this change since it is the only test that had to be modified due to the changes in this PR. The spirit of the test is unchanged but the spec had to be modified.
37cbd47
to
8494b3e
Compare
Another angle to this problem came up today. Although I'd say there's a general problem here with respect to packages bundled in the system library that are also in pypi, and there's a category of that problem that argparse falls into where the package is old and not getting updates. The
I think this might all be possible with this PR and the existing
When solving for python, the What I'm not sure already works [with this PR] is the solver finding a stub for an embedded package that comes from a component and having that add a request for that component to an existing request for the provider package. Edit: I have learned a little more about our situation with argparse, and it involves a DCC that includes python but we're not "embedding" python in this case but allowing the DCC python and "real" python to coexist in the environment. To get things to (mostly) work some |
From the meeting today:
install:
components:
- name: cmpt
embedded:
- name:component
- name:all # all components
- name # this is an error, must specify components
embedded:
- pkg: name |
b793bd9
to
9822d73
Compare
I'm not convinced that the extra complexity of implementing this adds any value over what this PR is already doing. If So for example: pkg: demo/1.0.0
install:
embedded:
- pkg: embedded1/1.0.0
- pkg: embedded2/1.0.0 expands to: pkg: demo/1.0.0
install:
components:
- name: build
embedded:
- embedded1:build/1.0.0
- embedded2:build/1.0.0
- name: run
embedded:
- embedded1:run/1.0.0
- embedded2:run/1.0.0
embedded:
- pkg: embedded1/1.0.0
install:
components:
- name: build
- name: run
- pkg: embedded2/1.0.0
install:
components:
- name: build
- name: run Changing how the default build and run components are generated feels a bit out of scope for this PR. |
|
||
/// Parse a package name with optional components and optional version. | ||
/// | ||
/// The package name must either be followed by a `/` or the end of input. |
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 line seems like it's wrong for this function...
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.
It's not wrong but I changed it a little.
Remove embedded from ComponentSpec. This new field provides a way to specify which components of the embedded package are actually present inside the component(s) of the parent package. It is now expected that an embedded package is defined only once in an entry in InstallSpec.embedded. Now that install.components[].embedded is removed, it is no longer possible to nest embedded packages. Signed-off-by: J Robert Ray <[email protected]>
This is needed to describe which components are being embedded. Signed-off-by: J Robert Ray <[email protected]>
Build on the ComponentsMissing type to be able to detect when missing components are from some package not embedding them (or requests for them are missing). .. TRY victim/1.0.0/3I42H3S6 - fake-pkg embeds real-pkg but does not provide all required components: needed comp2, have comp1 Signed-off-by: J Robert Ray <[email protected]>
If a package asks for some component `dep:comp1` and `dep` is already in the solution because it comes embedded in some other package `other`, but `dep:comp1` is embedded in some component of `other:comp2` and no request for `other:comp2` is present, then in order to satisfy the requirement for `dep:comp1`, automatically detect this situation, find what component(s) of `other` provide the missing components, roll back the state to before `other` was resolved, and inject a request for those components. Signed-off-by: J Robert Ray <[email protected]>
Demonstrate functionality and correct solution for a request that mixes components and embedded components. Signed-off-by: J Robert Ray <[email protected]>
If embedded_components aren't explicitly defined, default to mapping the components of the embedded package(s) to the components of the host package with the same name(s). Signed-off-by: J Robert Ray <[email protected]>
If the embedded_components field was populated by calculating default values, don't serialize the "fabricated" value. Signed-off-by: J Robert Ray <[email protected]>
Verify the rest of the expected default behavior. Signed-off-by: J Robert Ray <[email protected]>
Demonstrate the expected behavior for packages that embed packages that define components that the host package does not define. It is up to the package author to manually assign the extra component to one or more host components. Signed-off-by: J Robert Ray <[email protected]>
Like VersionIdent but the Version is optional. Although strings without a version can be parsed into a VersionIdent, the knowledge of if the version was explicitly specified is lost. Signed-off-by: J Robert Ray <[email protected]>
This is needed to be able to specify the version number of an embedded package, in the event that two different versions of the same package are being embedded. Signed-off-by: J Robert Ray <[email protected]>
If an embedded package is explicitly defined in embedded_components, but no components are named, then treat this as if ":all" was given, since embedding an empty component set is not useful. Signed-off-by: J Robert Ray <[email protected]>
When validating, and adding requests, only add requests for the embedded package(s) that are enabled in the active component(s) of the host package. Signed-off-by: J Robert Ray <[email protected]>
It's now clearer how the original validation that was happening here can be translated into the new code. Refactor the validation code from EmbeddedPackageValidator since the logic is identical in these two places. Signed-off-by: J Robert Ray <[email protected]>
This validation is possible again, using `packages_matching_embedded_component`. Signed-off-by: J Robert Ray <[email protected]>
The EmbeddedComponentsList type is now called ComponentEmbeddedPackageList, as this clarifies it is expected to be nested in a component, and there was already a EmbeddedPackagesList type. Signed-off-by: J Robert Ray <[email protected]>
This new test reaches the validation check at the end of PkgRequirementsValidator::validate_request_against_existing_state which is checking that a new component requirement is not bringing in a new embedded package that conflicts with the current state. Signed-off-by: J Robert Ray <[email protected]>
Changing name to just "embedded" as discussed. Signed-off-by: J Robert Ray <[email protected]>
As discussed. It is no longer possible to imply :All by leaving the components set empty. Signed-off-by: J Robert Ray <[email protected]>
Signed-off-by: J Robert Ray <[email protected]>
Addresses the question from #742.
The original spec schema had multiple places where an embedded package could be defined:
It becomes confusing if the embedded package also has components. Where do those components get defined? How can the parent package say which of its components contains the embedded packages' components?
A motivating example
The package boost contains a lot of headers only needed during build so it benefits from having those files live only in the "build" component. A second package, boost-python, is also a full build of boost (plus extra python things), so it needs to embed boost. Since the boost-python package is a full build of boost, it also benefits from having the headers only live in the "build" component. It needs to be able to say that the
boost-python:build
component embeds theboost:build
component.This shows how the boost-python spec can now be written. The embedded boost package is defined once under
install.embedded
and its components are defined here. The newinstall.components[].embeded_components
field is used to specify for each component of boost-python, which components of boost are present.Solver logic changes
There can be a situation where a certain combination of requests will cause the solver to fail even though there is nothing "wrong" about those requests.
First, there needs to be a request for a specific component of a real package:
real-package:comp1
. Assumingreal-package:comp1
exists, this request will solve successfully.Then, some other package needs to exist that embeds
real-package
, let's call itfake-package
. Assumefake-package:comp1
embedsreal-package:comp1
and other components exist insidefake-package
.Now, add a request for a specific component of
fake-package
that isn'tcomp1
:fake-package:comp2
. As in,spk env real-package:comp1 fake-package:comp2
.The problem arises when
fake-package
replacesreal-package
in the solve, but onlyfake-package:comp2
is part of the solution, because this does not include (embed)real-package:comp1
.The solver now needs to detect that the current set of requests do not bring in all the necessary components of the parent package in order to have the required components of the embedded package satisfied. It effectively needs to translate the
real-package:comp1
request into a request forfake-package:comp1
. When this situation is detected, the state will be rolled back to just beforefake-package
is added to the solution, and a request forfake-package:comp1
is added. This combines with the explicit request forfake-package:comp2
, making the merged request intofake-package:{comp1,comp2}
. Now whenfake-package
is added to the solution it will includecomp1
and the request forreal-package:comp1
will be satisfied.