clojars

emccue 2023-04-01T15:24:51.333169Z

Are there any maven experts here? I've been writing my own pom parser + dependency resolver and the full flow of how POMs are resolved is still super wacky to me and idk if I get it (details in thread)

emccue 2023-04-01T15:25:41.852619Z

So first, I know how to parse the content literally represented in the POM

emccue 2023-04-01T15:25:54.010459Z

from there, I think the next step is "find the parents"

emccue 2023-04-01T15:27:42.121699Z

Then there is a step where the inheritance tree is collapsed and properties are subbed

emccue 2023-04-01T15:27:58.705189Z

and at some point <scope> import is handled in some way?

emccue 2023-04-01T15:28:30.234659Z

which is recursive, since i think I need to fully parse that pom too!?!

emccue 2023-04-01T15:28:50.408519Z

and then i need to collapse dependencies and dependencyManagement

emccue 2023-04-01T15:28:57.321389Z

/**
 *   POM (xml file)
 *        |
 *        |   Parse XML
 *        V
 *     PomInfo
 *        |
 *        |   Fetch all parent poms
 *        V
 * ChildHavingPomInfo
 *        |
 *        |   Squash parent poms and replace properties
 *        V
 * EffectivePomInfo
 *        |
 *        |   Fetch BOMs and other deps with import scope
 *        v
 *      ?????
 *        |
 *        |   Collapse dependency and dependency-management
 *        v
 *      PomManifest  [ contains just list of deps ]
 *        |
 *        |   Normalize snapshots and version ranges
 *        v
 *      PomManifest  [ type unchanged ]
 */

emccue 2023-04-01T15:30:41.806809Z

i'm just kinda lost

emccue 2023-04-01T15:31:22.711889Z

and i've been reading both other code doing this same task and the documentation - its helping but its painful

Alex Miller (Clojure team) 2023-04-01T15:32:16.968519Z

There are a couple maven books they wrote a long while ago that might actually cover some of this

Alex Miller (Clojure team) 2023-04-01T15:33:02.531399Z

You didn’t mention property resolution above but that also should be in there

Alex Miller (Clojure team) 2023-04-01T15:33:17.536829Z

But yes, it is painful

emccue 2023-04-01T15:34:20.001469Z

yeah i squished property resolution and parent poms into the same step

emccue 2023-04-01T15:34:31.919749Z

maybe they should be distinct

emccue 2023-04-01T15:37:16.711709Z

this is the "flow" i got rn (huge code chunks)

record PomInfo(
        PomGroupId groupId,
        PomArtifactId artifactId,
        PomVersion version,

        List<PomDependency> dependencies,

        PomParent parent,

        List<PomDependency> dependencyManagement,

        List<PomProperty> properties,

        PomPackaging packaging
) {
}
| v
record ChildHavingPomInfo(
        PomGroupId groupId,
        PomArtifactId artifactId,
        PomVersion version,
        List<PomDependency> dependencies,
        List<PomDependency> dependencyManagement,
        List<PomProperty> properties,
        PomPackaging packaging,
        Optional<ChildHavingPomInfo> child
) {
| v
record EffectivePomInfo(
        PomGroupId groupId,
        PomArtifactId artifactId,
        PomVersion version,
        List<PomDependency> dependencies,
        List<PomDependency> dependencyManagement,
        PomPackaging packaging
) {

    static EffectivePomInfo from(final ChildHavingPomInfo childHavingPomInfo) {
        var properties = new LinkedHashMap<String, String>();

        var top = childHavingPomInfo;
        while (top != null) {
            for (PomProperty property : top.properties()) {
                properties.put(property.key(), property.value());
            }
            top = top.child().orElse(null);
        }

        Function<String, String> resolve =
                str -> resolveProperties(properties, str);

        Function<PomDependency, PomDependency> resolveDep = dependency ->
                new PomDependency(
                        dependency.groupId().map(resolve),
                        dependency.artifactId().map(resolve),
                        dependency.version().map(resolve),
                        dependency.exclusions().stream()
                                .map(exclusion -> new PomExclusion(
                                        exclusion.groupId().map(resolve),
                                        exclusion.artifactId().map(resolve)
                                ))
                                .collect(Collectors.toUnmodifiableSet()),
                        dependency.type().map(resolve),
                        dependency.classifier().map(resolve),
                        dependency.optional().map(resolve),
                        dependency.scope().map(resolve)
                );

        PomGroupId groupId = PomGroupId.Undeclared.INSTANCE;
        PomVersion version = PomVersion.Undeclared.INSTANCE;
        PomPackaging packaging = PomPackaging.Undeclared.INSTANCE;
        var dependencies = new LinkedHashMap<PomDependencyKey, PomDependency>();
        var dependencyManagement = new LinkedHashMap<PomDependencyKey, PomDependency>();

        top = childHavingPomInfo;
        while (top != null) {
            if (top.groupId() instanceof PomGroupId.Declared) {
                groupId = top.groupId().map(resolve);
            }
            if (top.version() instanceof PomVersion.Declared) {
                version = top.version().map(resolve);
            }
            if (top.packaging() instanceof PomPackaging.Declared) {
                packaging = top.packaging().map(resolve);
            }

            top = top.child().orElse(null);
        }

        groupId.ifDeclared(value -> properties.put("project.groupId", value));
        version.ifDeclared(value -> properties.put("project.version", value));


        var artifactId = childHavingPomInfo.artifactId().map(resolve);
        artifactId.ifDeclared(value -> properties.put("project.artifactId", value));

        top = childHavingPomInfo;
        while (top != null) {

            top.dependencies()
                    .forEach(dependency -> {
                        var newDep = resolveDep.apply(dependency);
                        dependencies.put(PomDependencyKey.from(newDep), newDep);
                    });

            top.dependencyManagement()
                    .forEach(dependency -> {
                        var newDep = resolveDep.apply(dependency);
                        dependencyManagement.put(PomDependencyKey.from(newDep), newDep);
                    });

            top = top.child().orElse(null);
        }

        return new EffectivePomInfo(
                groupId,
                artifactId,
                version,
                dependencies.values().stream().toList(),
                dependencyManagement.values().stream().toList(),
                packaging
        );
    }
| v
record PomManifest(
        @Override List<Dependency> dependencies
) implements Manifest {

Alex Miller (Clojure team) 2023-04-01T15:38:24.926479Z

I'm curious why you're doing this :)

lread 2023-04-01T15:38:26.029429Z

For what it is worth I find the maven APIs overwhelming too. I have learned a thing or two by reading https://github.com/clj-commons/pomegranate and https://github.com/clojure/tools.deps sources.

emccue 2023-04-01T15:38:33.316729Z

bright side - i have successfully detached resolution from all this nonsense in the same way tools.deps has

emccue 2023-04-01T15:39:31.330679Z

> I'm curious why you're doing this Mental illness, tbh

Alex Miller (Clojure team) 2023-04-01T15:39:44.271129Z

every time I read the Maven source I just want to throttle someone. it's such a paragon of 00's Java style, but it's all just data that could be maps and you would need none of the "abstractions" and "patterns"

🫠 1
emccue 2023-04-01T15:40:13.233969Z

but the thought is that java should have an actual build tool built in

Alex Miller (Clojure team) 2023-04-01T15:40:55.187999Z

I think maybe we've finally reached a point where new languages actually think about this early

emccue 2023-04-01T15:41:32.710749Z

and one part of that would be a resolver

emccue 2023-04-01T15:41:40.430249Z

I have no power to make that happen

emccue 2023-04-01T15:41:52.966439Z

but I do have the ability to experiment in a public way

Alex Miller (Clojure team) 2023-04-01T15:42:14.609929Z

I remember well building Java applications before Maven and repos existed

Alex Miller (Clojure team) 2023-04-01T15:42:49.324429Z

it was not fun

lread 2023-04-01T15:42:58.400049Z

Trying to make sense of the Maven APIs put me in a similar mood to when I tried to make sense of Windows shell escaping rules.

lread 2023-04-01T15:44:31.432719Z

And continuous integration YAML config.

emccue 2023-04-01T15:45:05.904069Z

yeah there is stuff maven does like "every repo is checked for every library" that feels insane

emccue 2023-04-01T15:45:27.194739Z

and I don't feel a strong need to recreate

emccue 2023-04-01T15:45:52.718749Z

pretty sure I can just attach a list of repos to check to the coordinate.

Alex Miller (Clojure team) 2023-04-01T15:47:22.230839Z

it's not only insane, it has all kinds of security issues

emccue 2023-04-01T15:49:00.761549Z

right now this is my conception of a coordiante

public interface Coordinate {

    default Coordinate normalize(Library library, Cache cache) {
        return this;
    }

    enum VersionComparison {

        INCOMPARABLE,
        GREATER_THAN,
        LESS_THAN,
        EQUAL_TO;


        public static VersionComparison fromInt(int comparisonResult) {
            return comparisonResult == 0 ? EQUAL_TO : comparisonResult > 0 ? GREATER_THAN : LESS_THAN;
        }
    }


    CoordinateId id();

    Manifest getManifest(Library library, Cache cache);

    /**
     * Gets the location of the given library on disk, assuming the library was located
     * with this coordinate.
     *
     * <p>
     *     If the library is not downloaded on disk, this method will do so before returning.
     * </p>
     */
    Path getLibraryLocation(Library library, Cache cache);

    default Optional<Path> getLibrarySourcesLocation(Library library, Cache cache) {
        return Optional.empty();
    }

    default Optional<Path> getLibraryDocumentationLocation(Library library, Cache cache) {
        return Optional.empty();
    }
}

emccue 2023-04-01T15:51:32.481439Z

Manifest is just a named List<Dependency> and thats what all this pom BS collapses into

emccue 2023-04-01T15:51:38.641629Z

public record MavenCoordinate(
        Version version,
        List<MavenRepository> repositories,
        List<Scope> scopes
) implements Coordinate {

emccue 2023-04-01T15:52:30.859359Z

(scopes...idk need to read one of those books i guess)

emccue 2023-04-01T15:52:58.403319Z

but the big ? for me at this level is whether exclusions are conceptually part of the coordinate

Alex Miller (Clojure team) 2023-04-01T16:04:26.411649Z

I would say yes

Alex Miller (Clojure team) 2023-04-01T16:05:22.676309Z

scopes tell you how to make different kinds of classpaths out of the same set of dependencies (at least that's how I think about it)

Alex Miller (Clojure team) 2023-04-01T16:07:05.099919Z

you seem to be missing classifiers too unless that's part of your version. classifiers are a weird little world of their own as they bring in conditional stuff

emccue 2023-04-01T16:08:33.873629Z

they are not - i was just thinking of source/javadocs/none

emccue 2023-04-01T16:08:46.337569Z

like on maven repository there is this method

emccue 2023-04-01T16:08:56.588779Z

abstract InputStream getFile(
            Library library,
            Version version,
            Classifier classifier,
            Extension extension
    ) throws LibraryNotFound;

Alex Miller (Clojure team) 2023-04-01T16:09:17.785129Z

they are really important for a small percentage of Java libs

Alex Miller (Clojure team) 2023-04-01T16:09:23.457139Z

particularly those with native stuff

Alex Miller (Clojure team) 2023-04-01T16:10:13.365679Z

the two big conditionals they cover usually are jdk (which is rarely used anymore and shouldn't even be needed now that we have multi release jars), and native architecture

Alex Miller (Clojure team) 2023-04-01T16:11:15.787389Z

classifiers are weird in that all classifiers technically share the same pom and have the same coordinate. that they wedged docs and source into that is a bit of a hack

emccue 2023-04-01T16:11:49.264729Z

so i can conceive of how to add that to the MavenCoordinate - like use this classifier for the jar - but how would that affect resolution?

emccue 2023-04-01T16:11:56.301929Z

record MavenCoordinateId(Version version)
        implements CoordinateId {
}

emccue 2023-04-01T16:12:32.707489Z

rn this is what I am keying things on in my (non-functioning) clone of t.deps algo

Alex Miller (Clojure team) 2023-04-01T16:12:45.612789Z

in deps I decided to make it part of the artifact: group/artifact[$classifier]

Alex Miller (Clojure team) 2023-04-01T16:13:43.948449Z

vs making it part of the version

emccue 2023-04-01T16:14:37.853779Z

ugh so thats not suuuper fun

emccue 2023-04-01T16:14:55.451359Z

Library is currently this

emccue 2023-04-01T16:15:08.516409Z

public record Library(
        Group group,
        Artifact artifact
) {

emccue 2023-04-01T16:16:11.211339Z

which is already a little maven specific, but slapping Classifier on there feels gross...

Alex Miller (Clojure team) 2023-04-01T16:16:18.653319Z

classifier is really a variant of an artifact that is specific to jdk/arch/something else (we use "aot" as a classifier in a few of the contrib libs)

emccue 2023-04-01T16:16:56.109439Z

I can make Library an interface - its basically just a key in a hashmap

Alex Miller (Clojure team) 2023-04-01T16:16:58.993309Z

clojure itself publishes both the "normal" compiled jar, but also a "slim" classifier jar with just source

emccue 2023-04-01T16:17:25.845699Z

just questioning how to handle sources + docs in that world

Alex Miller (Clojure team) 2023-04-01T16:17:41.971709Z

well like I said, those are imo hacks - they are also classifiers

Alex Miller (Clojure team) 2023-04-01T16:18:36.256389Z

really, Maven should have had a way to associate some kind of ancillary information (like Clojure metadata) to an artifact

emccue 2023-04-01T16:19:34.728179Z

gradle has that with its module metadata

emccue 2023-04-01T16:33:38.528949Z

you see why im thinking about this? I think tools.deps does its job for clojure, but would be neat if a foundational part of modern programming didn't elicit reactions like > I just want to throttle someone. and > a similar mood to when I tried to make sense of Windows shell escaping rules.

Alex Miller (Clojure team) 2023-04-01T16:47:02.729829Z

Fight the good fight :)

borkdude 2023-04-01T20:25:42.465799Z

@emccue are you writing your own version of maven and why? :)

emccue 2023-04-01T21:33:09.586629Z

@borkdude of the resolver, not the build tool

borkdude 2023-04-01T21:41:05.878489Z

@emccue and why, if I may ask?

borkdude 2023-04-01T21:45:19.865399Z

Right, I'm asking since I considered doing something like this once when I had trouble compiling tools.deps.alpha (at the time) to native

borkdude 2023-04-01T21:45:40.021199Z

so I wondered what your motivations were

emccue 2023-04-01T21:45:43.212089Z

That would be a benefit

emccue 2023-04-01T21:46:05.303839Z

I'm not using any SPI or dynamic stuff and the number of deps is 1

emccue 2023-04-01T21:46:15.360679Z

So id imagine compiling to native would be easier

borkdude 2023-04-01T21:46:41.728979Z

what is the dep? perhaps it could also run with bb :)

emccue 2023-04-01T21:49:41.506759Z

No it is the dep

emccue 2023-04-01T21:49:57.853009Z

As of now the whole thing is zero dep

👏 1
emccue 2023-04-01T21:52:27.310569Z

You are historically a way more productive person, so I would welcome participation

borkdude 2023-04-01T21:58:36.872469Z

It would be interesting to have a pure .clj solution (even more so if it had a chance of being adopted by tools.deps). The resolution mechanism isn't changing all the time is it?

Alex Miller (Clojure team) 2023-04-01T21:59:23.333749Z

I’d consider it if it got me everything I needed, but really this is just part of the puzzle

emccue 2023-04-01T21:59:47.431109Z

I'm doing all my stuff in a style that would be idiomatically translatable enough

emccue 2023-04-01T22:00:33.963989Z

Idk if that would be useful or not for that goal but 🤷‍♀️

Alex Miller (Clojure team) 2023-04-01T22:00:43.163449Z

There’s so many features we can provide via the maven libs and it’s a lot to take all of that on

Alex Miller (Clojure team) 2023-04-01T22:01:11.340969Z

Which is why I’ve given up every time I’ve considered it :)

borkdude 2023-04-01T22:01:15.198059Z

yes, it's going to be work

borkdude 2023-04-01T22:03:08.010919Z

btw in the end I managed to compile tools.deps to native: https://github.com/babashka/tools-deps-native and this is one of the projects using it: https://github.com/babashka/tools.bbuild nothing more than a "look how far we can go" project at this point, in bb I'm still shelling out to Java via deps.clj to fetch deps