Skip to content

Swift Package Manager plugin for Kotlin#1

Open
PaGr0m wants to merge 3 commits into
masterfrom
pagrom/swift-package-manager-plugin
Open

Swift Package Manager plugin for Kotlin#1
PaGr0m wants to merge 3 commits into
masterfrom
pagrom/swift-package-manager-plugin

Conversation

@PaGr0m

@PaGr0m PaGr0m commented Jan 11, 2021

Copy link
Copy Markdown
Owner

No description provided.

@PaGr0m
PaGr0m requested a review from zoldater January 11, 2021 15:25
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this annotation and move sources to org.jetbrains.kotlin.gradle.plugin.spm package

// TODO: List<String> or String
@Input
@Optional
var defaultLocalization: List<String>? = null

@zoldater zoldater Jan 18, 2021

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add KDoc with references to corresponding SPM configuration parameters (for another properties too)

// }
}

class SystemLibrary(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make SystemLibrary implementing org.gradle.api.Named

@Optional
var resources: List<Resource>? = null

fun dependencies(configure: TargetDependencies.() -> Unit) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions with Kotlin lambdas parameters are visible only from kts scripts. To provide corresponding functionality to groovy scripts implement the overloading (see CocoapodsExtension.CocoapodsDependency#git(java.lang.String, groovy.lang.Closure<?>))

dependenciesProduct = dependency.products
}

fun exclude(configure: ExcludePath.() -> Unit) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as for dependencies

excludePaths = exclude.paths
}

fun sources(configure: SourceFile.() -> Unit) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as for dependencies

sourceFiles = sources.files
}

fun resources(configure: Resource.() -> Unit) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as for dependencies

// fun swiftSettings() {}
// fun linkerSettings() {}

class Resource() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It worth moving nested declarations into separate files because there are no private visibility restrictions.

@Optional
var localization: Localization? = null

constructor(path: String) : this() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to replace multiple constructors with default values or introduce builder for creating instances.

platforms = supportedPlatform.platforms
}

fun products(configure: Products.() -> Unit) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overloading with overloading groovy.lang.Closure (see CocoapodsExtension.CocoapodsDependency#git(java.lang.String, groovy.lang.Closure))

// TODO: cLanguageStandard: CLanguageStandard? = nil,
// TODO: cxxLanguageStandard: CXXLanguageStandard? = nil

fun platforms(configure: SupportedPlatform.() -> Unit) {

@zoldater zoldater Jan 18, 2021

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

|
|
\/

productsExecutables = product.executables
}

fun dependencies(configure: Dependencies.() -> Unit) {

@zoldater zoldater Jan 18, 2021

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/\
|
|

dependencies = dependency.dependencies
}

fun targets(configure: Targets.() -> Unit) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/\
|
|

}

// TODO: add fun with another params (supportedPlatform)
class SupportedPlatform() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, try to rethink this part. Supported is just the attribute of platform, but here SupportedPlatform is responsible for holding info like container and could be changed by its function calls.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType and its usages (for example, org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMultiplatformPlugin.kt:302)

platforms.add(platform)
}

enum class PlatformType {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MPP plugin contains proper API for platforms, re-use it (see org.jetbrains.kotlin.gradle.plugin.KotlinTarget#getPlatformType)

}

class Products {
var libraries = mutableListOf<ProductType.Library>()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use polymorphism

}

sealed class ProductType {
class Executable(@Input val name: String) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nesting is not required here but significantly complicates code-readability. Make sources more flat, please.

}
}

class Library(@Input val name: String) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

org.gradle.api.Named

class Dependencies {
var dependencies = mutableListOf<Package>()

fun `package`(url: String, version: String) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual overloading and `` quotes are not required. Use default parameters with @JvmOverloads annotation

}

/** TODO
* Все target оказались одинаковыми по сигнатурам,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А обрабатывать в плагине их тоже нужно одинаково? Думаю, что нет, поэтому важно дизайнить API с заделом на будущее.

}

class Targets {
// TODO: так и оставить как 3 списка?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Полиморфизм + 1 список. И классы, хранящие коллекцию каких-то сущностей, для удобства называют с суффиксом Container.

var libraries = mutableListOf<SystemLibrary>()
var binaries = mutableListOf<BinaryTarget>()

fun target(name: String, configure: Target.() -> Unit) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget about Groovy overloadings

@zoldater zoldater left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO для следующей итерации код-ревью:

  1. Распределить сущности по файлам и пакетам в разумных масштабах
  2. Посмотреть на некоторые сущности - таргеты, платформы и т.д., которые есть в MPP-плагине и могут быть переиспользованы. За конкретными примерами пиши в личку, подскажу.
  3. Не самые удачные абстракции с классами-контейнерами, которые аккумулируют в себе дофига логики. Скорее всего большая часть из них может быть заменена на проперть с project.container(<YourClassHere>::class.java) и обработку
  4. Если есть набор сущностей, похожих по сигнатурам, но отличающихся по какому-либо признаку, даже если сейчас этот признак не имеет значения, используем полиморфизм.
  5. Избегаем копи-пасты, излишней мутабельности и велосипедостроения.

/**
* TODO: comment
*
* @see [Product](https://docs.swift.org/package-manager/PackageDescription/PackageDescription.html#product)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Наверное стоит оставить только один источник (первый с оф. документации, второй с github), Но проблема в том, что в них разная информации (не актуальная)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пока это для нашего с тобой пользования, можешь не запариваться

val platforms = mutableListOf<SupportedPlatform>()

fun macOS(version: String) {
val platform = SupportedPlatform(Family.OSX, version)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Такое использование Family подойдет? Или имелось в виду, что стоит полностью переработать SupportedPlatforms ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Такая реализация вполне хороша


package org.jetbrains.kotlin.gradle.plugin.spm.utils

abstract class StringUnaryPlusContainer {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хорошая идея? Или не стоит так выносить логику?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Идея хорошая, но лучше закинуть в companion targetBlock'а и снизить видимость

class Executable(name: String) : AbstractProduct(name)

class Library(name: String, private val type: LibraryType) : AbstractProduct(name) {
enum class LibraryType {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Выбрал вариант с enum, как по мне, удобнее чем просто String. Но заставляет импортировать enum в gradle. Насколько это ок?
https://github.com/PaGr0m/spm-sample/blob/c50f8ea6d2cfc3e4e981c24788ce0d9208b97cbd/build.gradle.kts#L1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это лучше, чем сырая строка, но для up-to-date проверок нужна будет динамическая строковая проперть

@Input
lateinit var name: String

private val platformsContainer = project.container(SupportedPlatformBlock.SupportedPlatform::class.java)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Может тоже стоит создать интерфейс, как и с другими контейнерами? Хоть тут он особо не нужен, так как реализация одна. Но читаемость улучшит

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не нужно

@PaGr0m

PaGr0m commented Jan 31, 2021

Copy link
Copy Markdown
Owner Author

Пример DSL синтаксиса: build.gradle.kts example

}

data class SupportedPlatform(
@Input val type: Family,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Family - элемент перечисления, как Input его использовать нельзя. Пометь как Internal и добавь стринговую проперть c type.name как Input

}

abstract class AbstractTarget(private val name: String) : Target {
override fun getName(): String = name

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@get:Input

private val name: String,
val `package`: String,
val condition: String? = null
) : TargetDependency {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TargetDependency тоже сюда можно перенести

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants