All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robovm.compiler.config.ResolvedLocations Maven / Gradle / Ivy

The newest version!
package org.robovm.compiler.config;

import org.robovm.compiler.config.Config.QualifiedEntry;
import org.robovm.compiler.config.Config.QualifiedFile;
import org.robovm.compiler.util.XCFrameworkPlist;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * Extension to framework/library lists when XCFrameworks got expanded as result:
 * - framework search path will be extended with paths to particular arch/os folder from XCFramework
 * - framework list might be extended with framework list from XCFramework (there might be more than one and
 * names might differ from XCFramework)
 * - library list might be extended with static ones picked from XCFramework
 * this object keeps all these three modified lists
 * if XCFramework expansion is disabled -- it just proxy original entries
 */
final class ResolvedLocations {
    final List frameworkPaths;
    final List libs;
    final List frameworks;
    final List weakFrameworks;

    private ResolvedLocations(List frameworkPaths, List libs, List frameworks, List weakFrameworks) {
        this.frameworkPaths = frameworkPaths;
        this.libs = libs;
        this.frameworks = frameworks;
        this.weakFrameworks = weakFrameworks;
    }

    static class Resolver {
        //
        // input parameters
        //
        private final OS os;
        private final Arch arch;
        private List frameworkPaths;
        private List libs;
        private List frameworks;
        private List weakFrameworks;
        private Predicate qualifier = v -> true;
        private boolean xcFrameworkLookup;
        private List xcFrameworks;

        //
        // intermediate data
        //
        final List resolvedFrameworks = new ArrayList<>();
        final List resolvedWeakFrameworks = new ArrayList<>();
        final List resolvedFrameworkPaths = new ArrayList<>();
        final List resolvedLibs = new ArrayList<>();

        Resolver(OS os, Arch arch) {
            this.os = os;
            this.arch = arch;
        }

        Resolver setLibs(List libs) {
            this.libs = libs;
            return this;
        }

        Resolver setFrameworkPaths(List frameworkPaths) {
            this.frameworkPaths = frameworkPaths;
            return this;
        }

        Resolver setFrameworks(List frameworks) {
            this.frameworks = frameworks;
            return this;
        }

        Resolver setWeakFrameworks(List weakFrameworks) {
            this.weakFrameworks = weakFrameworks;
            return this;
        }

        Resolver setQualifier(Predicate qualifier) {
            this.qualifier = qualifier;
            return this;
        }

        Resolver setXcFrameworkLookup(boolean xcFrameworkLookup) {
            this.xcFrameworkLookup = xcFrameworkLookup;
            return this;
        }

        Resolver setXcFrameworks(List xcFrameworks) {
            this.xcFrameworks = xcFrameworks;
            return this;
        }

        ResolvedLocations resolve() {
            // apply qualifiers and get filtered entries in resolved* fields
            applyQualifiers();

            // xcFramework lookup is possible if: enabled, has paths to look in, has frameworks to loop up for
            if (xcFrameworkLookup && (frameworks != null && !frameworks.isEmpty()) &&
                    (frameworkPaths != null && !frameworkPaths.isEmpty())) {
                performLookup();
            }

            if (xcFrameworks != null && !xcFrameworks.isEmpty()) {
                // there are xcframeworks, expand them
                handleXCFrameworks();
            }

            return new ResolvedLocations(
                    resolvedFrameworkPaths.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resolvedFrameworkPaths),
                    resolvedLibs.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resolvedLibs),
                    resolvedFrameworks.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resolvedFrameworks),
                    resolvedWeakFrameworks.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(resolvedWeakFrameworks));
        }

        private void applyQualifiers() {
            if (frameworks != null && !frameworks.isEmpty()) {
                resolvedFrameworks.addAll(
                        frameworks.stream().filter(qualifier).map(f -> f.entry).collect(Collectors.toList())
                );
            }
            if (weakFrameworks != null && !weakFrameworks.isEmpty()) {
                resolvedWeakFrameworks.addAll(
                        weakFrameworks.stream().filter(qualifier).map(f -> f.entry).collect(Collectors.toList())
                );
            }
            if (frameworkPaths != null && !frameworkPaths.isEmpty()) {
                resolvedFrameworkPaths.addAll(
                        frameworkPaths.stream().filter(qualifier).map(f -> f.entry).collect(Collectors.toList())
                );
            }
            if (libs != null && !libs.isEmpty()) {
                resolvedLibs.addAll(
                        libs.stream().filter(qualifier).collect(Collectors.toList())
                );
            }
        }

        private void performLookup() {
            // resolved list of frameworks. if input framework is found to be xc one, it will be replaced
            // with either framework name from its plist or it will go to static libs

            // preparation - get list of qualified frameworkPaths
            // wrap each path it into list -- first element will point to path itself,
            // sequent elements to frameworks inside XCFrameworks resolved under this path
            List> frameworkPathChains = resolvedFrameworkPaths.stream()
                    .map(f -> new ArrayList() {{
                        this.add(f);
                    }})
                    .collect(Collectors.toList());

            List frameworkNames = new ArrayList<>(resolvedFrameworks);
            resolvedFrameworks.clear();
            for (String frameworkName : frameworkNames) {
                boolean xcFrameworkFound = false;
                for (List pathChain : frameworkPathChains) {
                    File path = pathChain.get(0);
                    File guess = new File(path, frameworkName + ".framework");
                    if (guess.isDirectory()) {
                        // regular framework found, stop resolving
                        break;
                    }
                    guess = new File(path, frameworkName + ".xcframework");
                    if (guess.isDirectory()) {
                        // xc framework found, process it stop resolving
                        processXCFramework(guess, pathChain, resolvedFrameworks, resolvedLibs);
                        xcFrameworkFound = true;
                        break;
                    }
                }
                if (!xcFrameworkFound) {
                    // either resolved .framework or it was not resolved -- means system framework
                    // add to the list of framework
                    resolvedFrameworks.add(frameworkName);
                }
            }

            // flatten framework path chain into simple list
            // will be added to the top of frameworks path from config
            resolvedFrameworkPaths.clear();
            resolvedFrameworkPaths.addAll(
                    frameworkPathChains.stream()
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList())
            );
        }

        private void handleXCFrameworks() {
            // qualify for given constraints (e.g. specific path etc)
            List xcFrameworkLocations = xcFrameworks.stream()
                    .filter(qualifier)
                    .map(f -> f.entry)
                    .collect(Collectors.toList());
            for (File xcFrameworkLocation : xcFrameworkLocations)
                processXCFramework(xcFrameworkLocation, resolvedFrameworkPaths, resolvedFrameworks, resolvedLibs);
        }

        private void processXCFramework(File xcFrameworkLocation, List destLocations, List destFrameworks,
                                        List destLibs) {
            try {
                String frameworkName = xcFrameworkLocation.getName();
                XCFrameworkPlist plist = XCFrameworkPlist.load(xcFrameworkLocation);

                // filter all libraries that match platform and slice arch
                Predicate acceptedLibsPredicate = l ->
                        l.getPlatform() == os && l.supportCpuArch(arch.getCpuArch()) &&
                                l.getVariant() == arch.getEnv();
                List matchingLibs = Arrays.stream(plist.getAvailableLibraries())
                        .filter(acceptedLibsPredicate)
                        .collect(Collectors.toList());
                if (matchingLibs.size() == 1) {
                    XCFrameworkPlist.Library library = matchingLibs.get(0);
                    String libPath = library.getPath();
                    if (libPath.endsWith(".framework")) {
                        // framework (static or dynamic framework)
                        // save path to it, save its name
                        File p = new File(xcFrameworkLocation, library.getIdentifier());
                        destLocations.add(p);
                        // add a name (might be different from frameworkName)
                        destFrameworks.add(libPath.substring(0, libPath.length() - ".framework".length()));
                    } else if (libPath.endsWith(".a")) {
                        // static library inside .xcframework, add it to libs
                        File staticLibFile = new File(new File(xcFrameworkLocation, library.getIdentifier()), libPath);
                        destLibs.add(new Config.Lib(staticLibFile.getAbsolutePath(), false));
                    } else {
                        String message = String.format(
                                "Unsupported library type %s matched %s %s, in '%s'",
                                libPath, os, arch.getEnv().getLlvmName(), frameworkName);
                        throw new IllegalArgumentException(message);
                    }
                } else if (matchingLibs.isEmpty()) {
                    String message = String.format(
                            "While building for %s %s, no library for this platform was found in '%s'",
                            os, arch, frameworkName);
                    throw new IllegalArgumentException(message);
                } else {
                    String message = String.format(
                            "Multiple libraries were found matching %s %s in '%s'",
                            os, arch, frameworkName);
                    throw new IllegalArgumentException(message);
                }
            } catch (IOException e) {
                // failed to load xcframework
                throw new IllegalArgumentException(
                        "Failed to handle xcframework " + xcFrameworkLocation.getAbsolutePath(), e);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy