From a6656a0cbab4bb28a99e37c4a735152aa7e1cfb8 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Mon, 27 Mar 2017 18:16:06 +0300 Subject: [PATCH] Improve samples (#396) --- .../kotlin/native/interop/gen/jvm/main.kt | 2 +- samples/csvparser/README.md | 7 +- samples/gitchurn/README.md | 11 +++ samples/gitchurn/build.sh | 27 ++++++++ samples/gitchurn/libgit2.def | 3 + samples/gitchurn/src/main.kt | 3 + .../gitchurn/src/org/konan/libgit/GitChurn.kt | 69 +++++++++++++++++++ .../src/org/konan/libgit/GitCommit.kt | 28 ++++++++ .../gitchurn/src/org/konan/libgit/GitDiff.kt | 40 +++++++++++ .../src/org/konan/libgit/GitRemote.kt | 11 +++ .../src/org/konan/libgit/GitRepository.kt | 58 ++++++++++++++++ .../gitchurn/src/org/konan/libgit/GitTree.kt | 45 ++++++++++++ samples/gitchurn/src/org/konan/libgit/git.kt | 28 ++++++++ samples/opengl/build.sh | 5 +- samples/opengl/opengl.def | 8 ++- 15 files changed, 336 insertions(+), 9 deletions(-) create mode 100644 samples/gitchurn/README.md create mode 100755 samples/gitchurn/build.sh create mode 100644 samples/gitchurn/libgit2.def create mode 100644 samples/gitchurn/src/main.kt create mode 100644 samples/gitchurn/src/org/konan/libgit/GitChurn.kt create mode 100644 samples/gitchurn/src/org/konan/libgit/GitCommit.kt create mode 100644 samples/gitchurn/src/org/konan/libgit/GitDiff.kt create mode 100644 samples/gitchurn/src/org/konan/libgit/GitRemote.kt create mode 100644 samples/gitchurn/src/org/konan/libgit/GitRepository.kt create mode 100644 samples/gitchurn/src/org/konan/libgit/GitTree.kt create mode 100644 samples/gitchurn/src/org/konan/libgit/git.kt diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index 5860a647599..4889b7a6d14 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -95,7 +95,7 @@ private fun ProcessBuilder.runExpectingSuccess() { } private fun Properties.getSpaceSeparated(name: String): List { - return this.getProperty(name)?.split(' ') ?: emptyList() + return this.getProperty(name)?.split(' ')?.filter { it.isNotEmpty() } ?: emptyList() } private fun Properties.getOsSpecific(name: String, diff --git a/samples/csvparser/README.md b/samples/csvparser/README.md index 8c1bc97bbdb..83d6ce628df 100644 --- a/samples/csvparser/README.md +++ b/samples/csvparser/README.md @@ -1,6 +1,6 @@ -# CVS parser +# CSV parser - This example shows how one could implement simple CSV reader and parser in Kotlin. + This example shows how one could implement simple comma separated values reader and parser in Kotlin. A sample data [European Mammals Red List for 2009] (https://data.europa.eu/euodp/en/data/dataset?res_format=CSV) from EU is being used. @@ -10,5 +10,4 @@ To run use ./CsvParser.kexe ./European_Mammals_Red_List_Nov_2009.csv 4 100 -Which will print fifth column (Family, zero-based index) in first 100 rows of the -CSV file. \ No newline at end of file +It will print fifth column (Family, zero-based index) in first 100 rows of the CSV file. diff --git a/samples/gitchurn/README.md b/samples/gitchurn/README.md new file mode 100644 index 00000000000..740ba3413e0 --- /dev/null +++ b/samples/gitchurn/README.md @@ -0,0 +1,11 @@ +# GIT frequency analyzer + + This example shows how one could perform statistics on Git repository. + +To build use `./build.sh` script without arguments (or specify `TARGET` variable if cross-compiling). + +To run use + + ./GitChurn.kexe + +It will print most frequently modified (by number of commits) files in repository. diff --git a/samples/gitchurn/build.sh b/samples/gitchurn/build.sh new file mode 100755 index 00000000000..0cad2a86ae7 --- /dev/null +++ b/samples/gitchurn/build.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +PATH=../../dist/bin:../../bin:$PATH +DIR=. + +CFLAGS_macbook=-I/opt/local/include +CFLAGS_linux=-I/usr/include +LINKER_ARGS_macbook="-L/opt/local/lib -lgit2" +LINKER_ARGS_linux="-L/usr/lib/x86_64-linux-gnu -lgit2" + +if [ x$TARGET == x ]; then +case "$OSTYPE" in + darwin*) TARGET=macbook ;; + linux*) TARGET=linux ;; + *) echo "unknown: $OSTYPE" && exit 1;; +esac +fi + +var=CFLAGS_${TARGET} +CFLAGS=${!var} +var=LINKER_ARGS_${TARGET} +LINKER_ARGS=${!var} +var=COMPILER_ARGS_${TARGET} +COMPILER_ARGS=${!var} # add -opt for an optimized build. + +interop -copt:$CFLAGS -def:$DIR/libgit2.def -target:$TARGET || exit 1 +konanc -target $TARGET src libgit2 -nativelibrary libgit2stubs.bc -linkerArgs "$LINKER_ARGS" -o GitChurn.kexe || exit 1 diff --git a/samples/gitchurn/libgit2.def b/samples/gitchurn/libgit2.def new file mode 100644 index 00000000000..10169da99ee --- /dev/null +++ b/samples/gitchurn/libgit2.def @@ -0,0 +1,3 @@ +headers = git2.h +linkerOpts = -lgit2 +excludedFunctions = diff --git a/samples/gitchurn/src/main.kt b/samples/gitchurn/src/main.kt new file mode 100644 index 00000000000..f35cc3a52a8 --- /dev/null +++ b/samples/gitchurn/src/main.kt @@ -0,0 +1,3 @@ +fun main(args: Array) { + org.konan.libgit.main(args) +} \ No newline at end of file diff --git a/samples/gitchurn/src/org/konan/libgit/GitChurn.kt b/samples/gitchurn/src/org/konan/libgit/GitChurn.kt new file mode 100644 index 00000000000..85e4eae0c36 --- /dev/null +++ b/samples/gitchurn/src/org/konan/libgit/GitChurn.kt @@ -0,0 +1,69 @@ +package org.konan.libgit + +import kotlinx.cinterop.* +import libgit2.* + +fun main(args: Array) { + if (args.size == 0) + return help() + + val workDir = args[0] + val limit = if (args.size > 1) { + args[1].toInt() + } else + null + + println("Opening…") + val repository = git.repository(workDir) + val map = mutableMapOf() + var count = 0 + val commits = repository.commits() + val limited = limit?.let { commits.take(it) } ?: commits + println("Calculating…") + limited.forEach { commit -> + if (count % 100 == 0) + println("Commit #$count [${commit.time.format()}]: ${commit.summary}") + + commit.parents.forEach { parent -> + val diff = commit.tree.diff(parent.tree) + diff.deltas().forEach { delta -> + val path = delta.newPath + val n = map[path] ?: 0 + map.put(path, n + 1) + } + diff.close() + parent.close() + } + commit.close() + count++ + } + println("Report:") + map.toList().sortedByDescending { it.second }.take(10).forEach { + println("File: ${it.first}") + println(" ${it.second}") + println() + } + + repository.close() + git.close() +} + +fun git_time_t.format() = memScoped { + val commitTime = alloc() + commitTime.value = this@format + ctime(commitTime.ptr)!!.toKString().trim() +} + + +private fun printTree(commit: GitCommit) { + commit.tree.entries().forEach { entry -> + when (entry) { + is GitTreeEntry.File -> println(" ${entry.name}") + is GitTreeEntry.Folder -> println(" /${entry.name} (${entry.subtree.entries().size})") + } + } +} + +fun help() { + println("Working directory should be provided") +} diff --git a/samples/gitchurn/src/org/konan/libgit/GitCommit.kt b/samples/gitchurn/src/org/konan/libgit/GitCommit.kt new file mode 100644 index 00000000000..98342780353 --- /dev/null +++ b/samples/gitchurn/src/org/konan/libgit/GitCommit.kt @@ -0,0 +1,28 @@ +package org.konan.libgit + +import kotlinx.cinterop.* +import libgit2.* + +class GitCommit(val repository: GitRepository, val commit: CPointer) { + fun close() = git_commit_free(commit) + + val summary: String get() = git_commit_summary(commit)!!.toKString() + val time: git_time_t get() = git_commit_time(commit) + + val tree: GitTree get() = memScoped { + val treePtr = allocPointerTo() + git_commit_tree(treePtr.ptr, commit).errorCheck() + GitTree(repository, treePtr.value!!) + } + + val parents: List get() = memScoped { + val count = git_commit_parentcount(commit) + val result = ArrayList(count) + for (index in 0..count - 1) { + val commitPtr = allocPointerTo() + git_commit_parent(commitPtr.ptr, commit, index).errorCheck() + result.add(GitCommit(repository, commitPtr.value!!)) + } + result + } +} \ No newline at end of file diff --git a/samples/gitchurn/src/org/konan/libgit/GitDiff.kt b/samples/gitchurn/src/org/konan/libgit/GitDiff.kt new file mode 100644 index 00000000000..828541857d4 --- /dev/null +++ b/samples/gitchurn/src/org/konan/libgit/GitDiff.kt @@ -0,0 +1,40 @@ +package org.konan.libgit + +import kotlinx.cinterop.* +import libgit2.* + +class GitDiff(val repository: GitRepository, val handle: CPointer) { + fun deltas(): List { + val size = git_diff_num_deltas(handle) + val results = ArrayList(size.toInt()) + for (index in 0..size - 1) { + val delta = git_diff_get_delta(handle, index) + results.add(GifDiffDelta(this, delta!!)) + } + return results + } + + fun close() { + git_diff_free(handle) + } + +} + +class GifDiffDelta(val diff: GitDiff, val handle: CPointer) { + + val status get() = handle.pointed.status.value + val newPath get() = handle.pointed.new_file.path.value!!.toKString() + val oldPath get() = handle.pointed.old_file.path.value!!.toKString() + + fun status(): String { + return when (status) { + GIT_DELTA_ADDED -> "A" + GIT_DELTA_DELETED -> "D" + GIT_DELTA_MODIFIED -> "M" + GIT_DELTA_RENAMED -> "R" + GIT_DELTA_COPIED -> "C" + else -> throw Exception("Unsupported delta status $status") + } + } + +} diff --git a/samples/gitchurn/src/org/konan/libgit/GitRemote.kt b/samples/gitchurn/src/org/konan/libgit/GitRemote.kt new file mode 100644 index 00000000000..0436cafb7b6 --- /dev/null +++ b/samples/gitchurn/src/org/konan/libgit/GitRemote.kt @@ -0,0 +1,11 @@ +package org.konan.libgit + +import kotlinx.cinterop.* +import libgit2.* + +class GitRemote(val repository: GitRepository, val handle: CPointer) { + fun close() = git_remote_free(handle) + + val url: String get() = git_remote_url(handle)!!.toKString() + val name: String = git_remote_name(handle)!!.toKString() +} \ No newline at end of file diff --git a/samples/gitchurn/src/org/konan/libgit/GitRepository.kt b/samples/gitchurn/src/org/konan/libgit/GitRepository.kt new file mode 100644 index 00000000000..cc0528bbecf --- /dev/null +++ b/samples/gitchurn/src/org/konan/libgit/GitRepository.kt @@ -0,0 +1,58 @@ +package org.konan.libgit + +import kotlinx.cinterop.* +import libgit2.* + +class GitRepository(val location: String) { + val arena = Arena() + val handle: CPointer = memScoped { + val loc = allocPointerTo() + git_repository_open(loc.ptr, location).errorCheck() + loc.value!! + } + + fun close() { + git_repository_free(handle) + arena.clear() + } + + fun remotes(): List = memScoped { + val remoteList = alloc() + git_remote_list(remoteList.ptr, handle).errorCheck() + val size = remoteList.count.value.toInt() + val list = ArrayList(size) + for (index in 0..size - 1) { + val array = remoteList.strings.value!!.reinterpret>>().pointed + val name = array[index].value!!.toKString() + val remotePtr = allocPointerTo() + git_remote_lookup(remotePtr.ptr, handle, name).errorCheck() + list.add(GitRemote(this@GitRepository, remotePtr.value!!)) + } + list + } + + fun commits(): Sequence = memScoped { + val walkPtr = allocPointerTo() + git_revwalk_new(walkPtr.ptr, handle).errorCheck() + val walk = walkPtr.value + git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL or GIT_SORT_TIME) + git_revwalk_push_head(walk).errorCheck() + generateSequence { + memScoped { + val oid = alloc() + val result = git_revwalk_next(oid.ptr, walk) + + when (result) { + 0 -> { + val commitPtr = allocPointerTo() + git_commit_lookup(commitPtr.ptr, handle, oid.ptr).errorCheck() + val commit = commitPtr.value!! + GitCommit(this@GitRepository, commit) + } + GIT_ITEROVER -> null + else -> throw Exception("Unexpected result code $result") + } + } + } + } +} \ No newline at end of file diff --git a/samples/gitchurn/src/org/konan/libgit/GitTree.kt b/samples/gitchurn/src/org/konan/libgit/GitTree.kt new file mode 100644 index 00000000000..a275ffb2fa1 --- /dev/null +++ b/samples/gitchurn/src/org/konan/libgit/GitTree.kt @@ -0,0 +1,45 @@ +package org.konan.libgit + +import kotlinx.cinterop.* +import libgit2.* + +class GitTree(val repository: GitRepository, val handle: CPointer) { + fun close() = git_tree_free(handle) + + fun entries(): List = memScoped { + val size = git_tree_entrycount(handle) + val entries = ArrayList(size.toInt()) + for (index in 0..size - 1) { + val treeEntry = git_tree_entry_byindex(handle, index)!! + val entryType = git_tree_entry_type(treeEntry) + val entry = when (entryType) { + GIT_OBJ_TREE -> memScoped { + val id = git_tree_entry_id(treeEntry) + val treePtr = allocPointerTo() + git_tree_lookup(treePtr.ptr, repository.handle, id) + GitTreeEntry.Folder(this@GitTree, treeEntry, treePtr.value!!) + } + GIT_OBJ_BLOB -> GitTreeEntry.File(this@GitTree, treeEntry) + else -> throw Exception("Unsupported entry type $entryType") + } + entries.add(entry) + } + entries + } + + fun diff(other: GitTree): GitDiff = memScoped { + val diffPtr = allocPointerTo() + git_diff_tree_to_tree(diffPtr.ptr, repository.handle, handle, other.handle, null).errorCheck() + GitDiff(repository, diffPtr.value!!) + } +} + +sealed class GitTreeEntry(val tree: GitTree, val handle: CPointer) { + val name: String get() = git_tree_entry_name(handle)!!.toKString() + + class Folder(tree: GitTree, handle: CPointer, val subtreeHandle: CPointer) : GitTreeEntry(tree, handle) { + val subtree = GitTree(tree.repository, subtreeHandle) + } + + class File(tree: GitTree, handle: CPointer) : GitTreeEntry(tree, handle) +} diff --git a/samples/gitchurn/src/org/konan/libgit/git.kt b/samples/gitchurn/src/org/konan/libgit/git.kt new file mode 100644 index 00000000000..a1d378ac746 --- /dev/null +++ b/samples/gitchurn/src/org/konan/libgit/git.kt @@ -0,0 +1,28 @@ +package org.konan.libgit + +import kotlinx.cinterop.* +import libgit2.* + +object git { + init { + git_libgit2_init() + } + + fun close() { + git_libgit2_shutdown() + } + + fun repository(location: String): GitRepository { + return GitRepository(location) + } +} + +fun Int.errorCheck() { + if (this == 0) return + throw GitException() +} + +class GitException : Exception(run { + val err = giterr_last() + err!!.pointed.message.value!!.toKString() +}) \ No newline at end of file diff --git a/samples/opengl/build.sh b/samples/opengl/build.sh index 8148f9676e0..d24447518dc 100755 --- a/samples/opengl/build.sh +++ b/samples/opengl/build.sh @@ -4,7 +4,7 @@ PATH=../../dist/bin:../../bin:$PATH DIR=. LINKER_ARGS_macbook="-framework OpenGL -framework GLUT" -LINKER_ARGS_linux="-L/usr/lib/x86_64-linux-gnu -lSDL2" +LINKER_ARGS_linux="-L/usr/lib/x86_64-linux-gnu -lglut -lGL -lGLU" if [ x$TARGET == x ]; then case "$OSTYPE" in @@ -22,4 +22,5 @@ var=COMPILER_ARGS_${TARGET} COMPILER_ARGS=${!var} # add -opt for an optimized build. interop -def:$DIR/opengl.def -target:$TARGET || exit 1 -konanc -target $TARGET opengl $DIR/OpenGlTeapot.kt -nativelibrary openglstubs.bc -linkerArgs "$LINKER_ARGS" -o OpenGlTeapot.kexe || exit 1 +# OpenGL stubs are rather big, so we need more heap space. +konanc -J-Xmx2G -target $TARGET opengl $DIR/OpenGlTeapot.kt -nativelibrary openglstubs.bc -linkerArgs "$LINKER_ARGS" -o OpenGlTeapot.kexe || exit 1 diff --git a/samples/opengl/opengl.def b/samples/opengl/opengl.def index 0ace79a6c88..74370d1adeb 100644 --- a/samples/opengl/opengl.def +++ b/samples/opengl/opengl.def @@ -1,2 +1,6 @@ -headers = GLUT/glut.h -compilerOpts = -framework OpenGL -framework GLUT +headers = +headers.osx = GLUT/glut.h +headers.linux = GL/glut.h +compilerOpts = +compilerOpts.osx = -framework OpenGL -framework GLUT +compilerOpts.linux = -I/usr/include