How specify a version for SPM for existing Objective-C git work - objective-c

I'm trying to understand the Swift Package Manager manifest - Package.swift when wrapping an existing work, with regards to a version. As an example, XMLDictioonary, is my fork where I wanted to expose it to SPM; so far I have:
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "XMLDictionary",
platforms: [
.macOS(.v10_13), .iOS(.v11), .tvOS(.v11),
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(name: "XMLDictionary", targets: ["XMLDictionary"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(name: "XMLDictionary", dependencies: [],
path: "XMLDictionary",
publicHeadersPath: "."),
]
)
which Xcode (12) supports so long as my version rule specifies master, but I'd like it to be 1.4.1 as is the upstream. I might at some point in the future diverge.
But adding what I think it should be
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(
url: "https://github.com/slashlos/XMLDictionary.git",
from: "1.4.1"
)
fails fails in Xcode SPM addition
and before that, locally the build fails:
error: cyclic dependency declaration found: XMLDictionary -> XMLDictionary
which I believe is telling me the dependency rule is on itself?
So how does one specify the version dependency ?

The Package.swift is the manifest of your framework and it has to be placed in the root directory. You did everything correctly.
If you want to import your framework in your app you should specify the Github repo url where to import the framework and eventually the version number. That version number is taken by the tags you have in your Github repo. If you set 1.4.1, SPM will checkout the code on your repository at the time it was tagged as 1.4.1 (the tag correspond to a specific commit!). BUT at that time the Package.swift wasn't there because you added it some commits later.
If you want use the semantic version to import your framework you should create a tag on your last commit. So when SPM will download that tag it's able to find your Package.swift file in there.

Related

How to handle package dependencies for some build targets when building with Copr?

I want to have a rpm package build with Copr1. My current build target list is Fedora 35, 36, rawhide and Centos 7 and Stream 8. I have not yet created the copr project.
Compiling on one of my machines, the package builds successfully on the Fedora variants with mock. The problem is that on Centos variants one of the build dependencies and some of its dependencies are not available. I have found appropriate srpm files and compiled them on one of my machines with the Centos Stream 8 (one of them required two custom patches). With those custom dependencies I am able to successfully compile the original package.
So just to be clear, the problem is that the spec file contains for example
BuildRequires: libsomething
where libsomething is available as a plain upstream package in some of the build targets while needs an additional custom repo for some other build targets.
The FAQ says the following about dependencies:
Can I depend on other packages, which are not in Fedora/EPEL?
Yes, they just need to be available in some yum repository. It can either be another Copr repo or a third-party yum repo (e.g jpackage). Click on “Edit” in your project and add the appropriate repositories into the “Repos” field. Packages from your project are available to be used at build time as well, but only for the project you are currently building and not from your other projects.
But this sounds like an all or nothing approach, and I absolutely do not want to override the already existing upstream packages, only provide them when they are missing.
So what strategy do people use to handle this?
Update: I have now created copr projects and made some attempts at building (after resolving dependencies of dependencies in several levels), but the problem is as I describe above. If I add copr://hlovdal/projectname as a build dependency then epel-8-x86_64 compiles fine because it is provided with the missing dependencies while fedora-35-x86_64 fails because the repository does not have any fedora packages. If I remove the repo epel fails while fedora succeeds.
I also attempted to add the base url from the corresponding /etc/yum.d.repo file, and only hardcode epel instead of $distname hoping that the fedora builds would just ignore non-existing/wrong repo setting, but the build does not like that and still fails.
1 Copr is Fedora's freely available build system.

How to add a dependency on a Pod library in KMM (Kotlin Multiplatform)?

So, I followed the Kotlin guideline and added the dependency from a remote repo like below:
pod("JSONModel") {
source = git("https://gitlab.com/jsonmodel/jsonmodel.git") {
branch = "key-mapper-class"
}
It does add the dependency, however instead of referring to the branch I point at gitlab, it points to the main branch. Any ideas why this might be happening?
Having a deeper look at the generated files, it feels as if this is a bug with native.cocoapods plugin within KMM. KMM adds the dependency as instructed on the shared library but it also adds the latest published version from cocoapods.

Why does updating Gradle break log4j imports?

I am attempting to update to Kotlin 1.4. In my build.gradle file, I have the following:
buildscript {
allprojects {
ext {
kotlin_version = "1.3.70"
ktor_version = "1.2.2"
junit_version = "5.4.2"
log4j_version = "2.11.2"
jackson_version = "2.9.9"
kafka_version = "2.3.0"
}
}
repositories {
maven {
url 'https://smartward.jfrog.io/smartward/gradle-dev'
credentials {
username = "${artifactory_user}"
password = "${artifactory_password}"
}
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.9.7"
}
}
and later on:
implementation(
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version",
"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version",
"org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version",
"org.apache.logging.log4j:log4j-api:$log4j_version",
// For JSON mapping
"com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version",
"com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version",
"com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version",
"com.natpryce:konfig:1.6.10.0",
"org.apache.kafka:kafka-clients:$kafka_version",
"io.ktor:ktor-server-netty:$ktor_version",
"io.ktor:ktor-locations:$ktor_version",
"io.ktor:ktor-jackson:$ktor_version",
"io.ktor:ktor-client-core:$ktor_version",
"io.ktor:ktor-client-apache:$ktor_version",
"io.ktor:ktor-client-json:$ktor_version"
)
My first step was to change kotlin_version to be "1.4.0". When running the build script, I was informed that Gradle needed to be updated as well. I did this, changing my gradle-wrapper.properties file (diff below):
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-all.zip
This now means that some of my log4j imports no longer work. Namely:
import org.apache.logging.log4j.core.Logger
import org.apache.logging.log4j.core.config.Configurator
I have attempted reverting to Kotlin 1.3.70, without reverting the Gradle update, and the issue persists, so I suspect a problem with Gradle, or my build script, but I'm not sure why or how to fix it. I have also attempted using Gradle 6.6, with the 4.17.1 version of org.jfrog.buildinfo:build-info-extractor-gradle, but the problem persists.
Use dependencyInsight to see what's going wrong
It sounds like what's happening is that the version of log4j that ends up being used isn't the version you were expecting.
Dependency version resolution can get pretty complicated, especially when you have lots of dependencies. Different things want different versions of the same dependency, but Gradle has to pick one version that will end up on the classpath. In general, it will pick the newest version from among all the versions that have been requested.
There are two reasons I can think of that upgrading Gradle might have changed the version of log4j that ends up being used:
Something in Gradle itself could be adding a dependency on log4j, and might now be requesting a newer version than was used in the older Gradle distribution.
On the other hand, it's possible that the way version conflicts are resolved has actually subtly changed in the newer version of Gradle.
Luckily, Gradle gives you some tools to help figure out what's going on. I would suggest comparing the output of the following command both before and after updating the Gradle version.
gradle dependencyInsight --dependency log4j
This will print out a tree-like report of everything that's using log4j, and will tell you why a particular version was selected. It might take some time to understand the report, especially if it's long, but it's worth reading through it carefully.
Use platform constraints to force the correct version
Projects like log4j are made up of several artifacts (log4j-api, log4j-core, etc). The process of resolving the various transitive dependency versions in your build can end up introducing versions that don't match each other. It's important to make sure that all the artifacts have matching versions.
To help solve this, log4j provides an additional 'bill of materials' artifact, log4j-bom. BOM artifacts don't contain any code, but they specify a list of dependencies, along with the versions that should be used.
Since version 5, Gradle lets you use BOM files to suggest or enforce versions for a set of dependencies. Applying a 'platform' dependency of this sort doesn't add or remove any actual dependencies to your build, but it does influence or control the versions of the dependencies you already have.
In your case, you could add the following to your dependencies:
dependencies {
implementation enforcedPlatform("org.apache.logging.log4j:log4j-bom:$log4j_version")
}
This adds the log4j-bom as an enforcedPlatform dependency, guaranteeing that every log4j dependency used in your application will always have the version you specify. This is a powerful tool and should help make sure you don't run into problems like this in future.
As per the official documentation of Log4j you need to link to both log4j-api and log4j-core to consume the package properly.

When I need to use dev_dependency

If I need any dependency I use them under dependencies like this
dependencies:
flutter:
sdk: flutter
provider: ^3.2.0
http: ^0.12.0+3
get_it: ^3.1.0
connectivity: ^0.4.6
email_validator: ^1.0.4
shared_preferences: ^0.5.4+8
google_maps_flutter: ^0.5.21+14
When I can use packages uder dev_dependencies? I don't know what I have to see in the packages so that I can decide which dependency goes to which category.
dev_dependencies:
Pub supports two flavors of dependencies: regular dependencies and dev_dependencies. Dev dependencies differ from regular dependencies in that dev_dependencies of packages you depend on are ignored.
For Example:
If you are developing a package, and you want some packages which are only imported for testing purpose and not the actual implementation, then such packages should go under dev_dependencies. When you are importing a package, pub gets every package that your imported package depends on. Since pub ignores dev_dependencies, the packages which were used for testing won't be fetched by pub.

How to add dependencies to a kotlin library?

I am trying to build a kotlin library for discord bots, which can be found at https://github.com/TheDrone7/discord-kt , published to jcenter (bintray link - https://bintray.com/thedrone7/discordKt/discord-kt). The library has a few dependencies of it's own as well.
When I add my own library to my test app, the library's dependencies were not installed and I started getting some errors. Is there a way to specify the library's dependencies so that they get automatically installed when a user uses my library?
EDIT: -
So basically my test app's build.gradle.kts file's dependencies section is given below
dependencies {
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.theDrone:discordKt:0.0.1a")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
And my library is dependent on the following packages: -
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0
org.java-websocket:Java-WebSocket:1.4.0
com.beust:klaxon:5.0.5
org.slf4j:slf4j-jdk14:1.7.26
now when I run my test app, it shows gives error that there is no class named WebSocketClient which is a part of the org.java-websocket:Java-WebSocket:1.4.0 package and is also the base of my entire library.
When I add the listed packages to my test app's dependencies, it works perfectly fine. So is there a way that I could define in my library that the apps using it will also automatically depend on the packages my library depends on?
You declared the Java-WebSocket library as a dependency of your library using the implementation configuration.
This configuration means: I need that for my code to work, but it's an implementation detail and it's thus not part of my public API, so users of my library won't have access to it in their compile classpath.
So Gradle, when it generates the pom.xml file for your library, adds Java-WebSocket as a runtime dependency, and not as a compile dependency.
Read the java-library plugin documentation, which explains all of that in details. Once you have understood it, use api instead of implementation in your library's build.gradle.kts file for the dependencies that are part of your API, and should thus be compile dependencies and not runtime dependencies:
api("org.java-websocket:Java-WebSocket:1.4.0")