Giter Club home page Giter Club logo

kbuild's Introduction

KBuild

I hope this may one day replace Gradle.

A build library, as opposed to a build system. Instead of having an independent coding system with different conventions, this is simply a Maven library you can import and use to build your projects from vanilla Kotlin using tools such as kscript, skate, and even the upcoming standard Kotlin script runner.

The biggest advantage of this approach is the code for building anything can be ultra-distributed. There's no vendor lock in at all; KBuild itself isn't necessary to use the plain Kotlin build file approach. It's just a set of tools for building Kotlin more conveniently from Kotlin itself, and as such, is really more a concept or idea more than any kind of library.

This is a WIP, and is not available in Maven yet. We'll get there.

Why not Gradle/Kobalt?

  • Gradle was built Groovy-first, and as such, its APIs are mostly untyped still
  • Plugins - The plugin systems allow the plugin developer to change the meaning of certain tasks without letting the end user easily track what they. It makes the execution path hard to follow. I prefer that my build script code act and be built the same way as the code I run.
  • More standard - The JVM is more standard than either Gradle or Kobalt, and with tools such as kscript, skate, and even the upcoming standard Kotlin script runner, we can run single Kotlin files directly.
    • Instead of running some special scripting environment, we're now doing things the standard JVM/Kotlin way, and IntelliJ is optimized for it. Editing build scripts is much more efficient and requires no plugins.
  • Environment-agnostic - Build tools written for plain Kotlin/Maven will also work in both Gradle and Kobalt without any changes.
  • Size of API - The API size of Gradle is staggering; as such, learning how Gradle works takes way longer than it should.

Q/A

  • If Gradle's so bad, why does this project use Gradle to build itself still?
    • Because Gradle isn't bad. It's actually pretty awesome. But that doesn't mean we can't do better, and I don't want to be developing something that requires itself to build until we're further along.
  • Isn't this just Kobalt all over again?
    • No. Read the description again. It's a build library, not system. It has no special scripting definition or anything like that. It's just a library. Also, the principles described below set the goals of this project apart from the others.
  • Why are you bothering to make this project?
    • See the section Why not Gradle/Kobalt?. Also, personal experience developing several Gradle plugins and weeks lost to working on build files in Kotlin Multiplatform.
  • How does it work?
    • Go take a look at the Structure section - everything here is a work in progress, however, so we'll see.
  • I want feature X!
    • Excellent! Make an issue about it and come discuss it on the Kotlin Slack channel called #kbuild.
  • How do I make a task?
    • A task is just a function, so just add a new task to your project object.

Principles

  • Minimal - The fewest number of parts that get the job done is best.
  • Clarity over conciseness - Short is good, understanding is better. The build system should make it easy to understand what is going on and prevent mistakes.
  • Project is API - There is no part of this project which isn't API. Users are intended to be able to read everything here and use everything here, though they shouldn't have to.
    • If the code isn't good enough to be exposed to the public, rewrite it.
    • Use names that are as clear as possible so that users don't need to go look at your sources.
    • No plugins. You can import other libraries which add functionality and use them, but there are no magical plugins which modify things outside of themselves.
  • Orient it around Kotlin - users of Kotlin should be able to pick it up more quickly if it's truly focused on using Kotlin and its principles.
    • Use Kotlin Standard Library to the fullest possible
    • Simplify concepts back into standard Kotlin where possible
      • Use functions for tasks. Gradle allowed for dynamic addition of functionality using this by adding before{} and after{}. However, clarity is more important than conciseness in this project, so instead it is encouraged that one override a function.
  • Use vendors' libraries directly where possible, exposing them to the user
    • If a vendor's library is too complicated, add a simple set of extensions, ideally no more than one file.
    • Avoid creating new types as much as possible
    • Example: Using org.apache.maven:maven-model for handling the structure of POM files.
    • Example: Using org.jetbrains.kotlin:kotlin-compiler-embeddable to compile Kotlin.
  • Minimize DSLs/Configure with Lambda
    • Functions over lambda configuration
      • Arguments make what is required is clearer and compile-time safe
      • Default values are clearer
    • Extra interface over real objects is discouraged; see principle 1
    • DSLs can be good under very specific conditions, but these are more rare than common.

Structure

Your project's definition file will be a single Kotlin file with @DependsOn("com.ivieleague:kbuild:<version>") at the top. It can be a .kt or a .kts, depending on what tool you're using to run it. Skate runs .kt, KScript runs both .kt and .kts, and the Kotlin Script Runner uses .kts.

Your project is then defined as an object implementing interfaces with defaults, like this:

@DependsOn("com.ivieleague:kbuild:<version>")

object MyProject: KotlinJVMModule, JvmRunnable {
    override val mainClass: String get() = "com.test.TestKt"
    override val root: File get() = File("temp")
    override val version: Version get() = Version(0, 0, 1)
    override val jvmJarLibraries: List<Library> get() = listOf(Kotlin.standardLibrary)
}

Now open that file within a REPL and you can do MyProject.build() or MyProject.run() or more!

There are several advantages to this kind of structure:

  • Going to the definition of how something works is easy; just control-click whatever you're wondering about.
    • With everything directly linked, finding the execution path is much simpler.
  • The different interfaces that define functions like .build() or .run() can require that another property be present
    • Their presence is validated at edit/compile time rather than run time, making it easier to use
    • IDEs will tell you what things are needed to get the project file working without searching through documentation
  • The different interfaces can provide implementations of some properties
    • Example: HasMavenInformation implements val jvmJarLibraries: List<Library> for you by looking at the dependencies in your Maven model.
  • Different interfaces can be used together easily.
  • You can override certain pieces functionality without having to rewrite the whole thing.

More examples can be found in the unit tests, like this one specifically:

Extending

Let's say we need to create a fat jar. How would we use somebody's tools to do that?

@DependsOn("com.ivieleague:kbuild:<version>")
@DependsOn("com.something:fat-jar-builder:<version>") //has fun fatJar(mainClass, listOfJars): File in it

object MyProject: KotlinJVMModule, JvmRunnable {
    override val mainClass: String get() = "com.test.TestKt"
    override val root: File get() = File("temp")
    override val version: Version get() = Version(0, 0, 1)
    override val jvmJarLibraries: List<Library> get() = listOf(Kotlin.standardLibrary)
    fun fatJar() = fatJar(this.mainClass, this.jvmJars) //jvmJars is part of the HasJvmJars interface, which is part of JvmRunnable
}

In this example, the creator of 'fat-jar-builder' doesn't even know KBuild exists. They don't need to. We can still easily use their functionality though!

If the creator of 'fat-jar-builder' wants to make integration easier, they could add a dependency in their library to KBuild and make this interface:

interface CreatesFatJar: JvmRunnable {
    fun fatJar() = fatJar(this.mainClass, this.jvmJars) //jvmJars is part of the HasJvmJars interface, which is part of JvmRunnable
}

Now we could use it on the other side like this:

@DependsOn("com.ivieleague:kbuild:<version>")
@DependsOn("com.something:fat-jar-builder:<version>") //has fun fatJar(mainClass, listOfJars): File in it

object MyProject: KotlinJVMModule, JvmRunnable, CreatesFatJar {
    override val mainClass: String get() = "com.test.TestKt"
    override val root: File get() = File("temp")
    override val version: Version get() = Version(0, 0, 1)
    override val jvmJarLibraries: List<Library> get() = listOf(Kotlin.standardLibrary)
}

Planned Features

  • Integration with IntelliJ
    • Have a function on the project object (probably something like prepare()) that creates/updates the IntelliJ project
    • Make special plugin for IntelliJ unnecessary if at all possible
  • Android
  • Kotlin Multiplatform
  • Kotlin JS - include NPM library
  • Kotlin Native - include C Library OR Objective C Library
  • Direct iOS support
    • Construct XCode Project with hooks back into this build system
    • Tasks which run the command line tools of XCode
  • Automatic Semantic Versioning - Let the build tool analyze your code declarations and determine if it is fully compatible with previous versions of the library and change the version accordingly.
  • Easy support for Maven library upload
  • Easy support for including Maven libraries, typescript repositories, and native libraries via CInterop, perhaps with a declaration added to the POM file so child projects could also use it
  • Meta-projects - If you have a set of projects that you wish to manage, you can use a meta-project to control them all from one place. This is less of a feature and more ensuring that the system doesn't get in the way by using any globals.
  • The Shattering - Eventually, this library should be split up into multiple artifacts to avoid downloading more dependencies than necessary.

Want to help?

Check out the issue list on GitHub; we have plenty to do. Understand, however, that your contributions need to be following the principles listed above. If they don't, I'll give you feedback and help you get it in line, but it won't be merged until it does. The cleanliness of this project is more important than features.

The other way you can help is kinda funny - the point of this system is to have build tools available in vanilla Kotlin. You can contribute simply by creating other open-source libraries that do parts of the build process.

kbuild's People

Contributors

unknownjoe796 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

kbuild's Issues

Kotlin JS Support

Create an interface called KotlinJSModule which has HasJarLibraries.

I don't know all of the details of how this should look; I leave it to whoever works on this issue.

IntelliJ Project Files creation

I'll be creating an interface that, when implemented, adds a function to get IntelliJ project files set up.

I'll be referencing the Skate Kotlin project for creating JVM IntelliJ project files, as well as referencing the project files created by IntelliJ when using a multiplatform system. This needs to work for all JAR library types.

Android support

Some specifications:

  • Should be in the package com.ivieleague.kbuild.android
  • Should be called AndroidModule
  • Should have functions for:
    • Handling resources, probably called prepareResources(), intended to be used before editing
    • Building
    • Creating/Signing an APK
  • Should be fully compatible with HasMavenInformation

Possibilities for Implementation:

  • Don't use the Android build library; use the binaries directly as specified here
  • Do use the Android build library found at 'com.android.tools.build:builder:2.0.0-alpha3'.

Things you can reference:

Kotlin Native

Modules for Kotlin Native will take some serious thought, as they have a large number of targets. I think we should probably do an individual project object per target.

This will also have to download the current version of Kotlin Native if it is not available, ideally into the same location the Gradle plugin installs it.

Multiplatform Support

Some thoughts:

  • We need the ability to declare a common code module
  • We need the ability to use the common code module in platform modules
  • Declaring dependencies in the common code module such that the platform modules using it can find the platform-specific version of the dependency could be quite tricky. I believe Gradle has some magical metadata system for doing that; perhaps we should also support the same?
  • It would be great to make it possible to easily declare, 'Make modules for every possible platform given that the dependencies are available for them'

Keychain

It is a common requirement in build files to access authentication credentials for uploading and deploying things.

Current interface idea:

object Keychain {
  var file = File(System.getProperty("user.home"))
  operator fun get(key: String): String { ... }
  operator fun set(key: String, value: String): String { ... }
}

This accesses an encrypted file.
The first time the Keychain is accessed, the system will request the encryption password from standard input, repeating until its correct or the program is manually terminated.

Ability to reference common Maven repositories by constant instead of manually

It would be nice to include things like JCenter and JitPack extremely easily. This could be done super easily like this:

import org.apache.maven.model.Model
...
Model().apply {
  repositories = listOf(Repositories.jcenter, Repositories.jitpack)
}

I'd recommend the creation of:

object Repositories {
  val jcenter: Repository = ...
  val jitpack: Repository = ...
}

This is really easy to do, but this is a first good task for somebody else to take who wants to get familiar with the project.

Don't forget to create a unit test that uses it!

Easy JUnit support

This one shouldn't be too hard, though it will be harder than issue #5.

You will probably need the following to work on this:

  • Some experience with working around build systems
  • Some experience with using JUnit
  • A willingness to research or knowledge of how to run JUnit manually
  • Some willingness to get familiar with the principles of KBuild, especially how it models code modules. Shouldn't take too long, you'll likely need to review most of the publicly exposed interfaces.

We need JUnit support.

My recommended path is this:

  • Create a JUnitModule interface
    • It should require a HasJvmJars module as a value
    • It should require a source root for the tests with a reasonable default
    • It should have a function called test(), which runs all of the tests
    • It should have a function called test(classOrTestName: String), which runs a subset of tests
    • Those functions should throw if the test fails
    • JUnit should be set up to output reports to a folder

Goals:

  • The end-user should be able to add dependencies just for testing with ease
  • The module should not be used to extend the end users' main module because IntelliJ views them as separate modules. Making it a separate module will ease IntelliJ support.

Maven Deployment

It needs to be possible to deploy any object with HasMavenInformation to a Maven repository, as well as to deploy to Maven local (.m2/repository).

There are multiple possibilities for this; I imagine the best one looks something like this:

val myRemoteRepository = RemoteRepository("bintray/joseph", "https://....")
object MyProject : HasMavenInformation ... { ... }
myRemoteRepository.push(MyProject)

Any thoughts?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.