Skip to content

Commit

Permalink
feat: implement cmake linker
Browse files Browse the repository at this point in the history
  • Loading branch information
Myriad-Dreamin committed Sep 18, 2024
1 parent e5434f0 commit 8ae23cc
Show file tree
Hide file tree
Showing 22 changed files with 386 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ node_modules
.bloop
dist
target
cmake-build-*
*.log

config.json
26 changes: 26 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

if (POLICY CMP0048)
cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)
cmake_minimum_required(VERSION 3.16)

project(cosmo
VERSION 0.1.0
DESCRIPTION "Cosmo Project"
LANGUAGES C CXX)

# import cmake compilation features I
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to")
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# import cmake compilation features II
include(cmake/feat-exception.cmake)
include(cmake/feat-rtti.cmake)
include(cmake/Cosmo.cmake)
# include(cmake/feat-use-lld.cmake)

## ~ project components
cosmo_import_artifact()
add_subdirectory(cmd)
32 changes: 32 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"version": 5,
"cmakeMinimumRequired": {
"major": 3,
"minor": 16,
"patch": 0
},
"configurePresets": [
{
"name": "relwithdebinfo-ninja-clang",
"displayName": "RelWithDebInfo and Ninja",
"description": "build with Clang Toolchain and Ninja build system",
"generator": "Ninja",
"binaryDir": "${sourceDir}/cmake-build-relwithdebinfo",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "relwithdebinfo-vs-clang",
"displayName": "RelWithDebInfo and Visual Studio",
"description": "build with Clang Toolchain and Visual Studio build system",
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/cmake-build-relwithdebinfo",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
}
]
}
21 changes: 21 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Build Cosmo Compiler

Lists Presets:

```shell
cmake --list-presets=all .
```

Generate Preset:

```shell
cmake --preset=relwithdebinfo-ninja-clang .
```

Build with Preset:

```shell
cmake --build cmake-build-relwithdebinfo --config RelWithDebInfo --target xxx
```

Use `xxx` to specify the target you want to build, such as `all`.
8 changes: 8 additions & 0 deletions cmake/Cosmo.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

function(cosmo_import_artifact)
## ~ cosmo generated code
message(STATUS "${CMAKE_CURRENT_SOURCE_DIR}/target/cosmo/release")
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/target/cosmo/release")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/target/cosmo/release")
endif()
endfunction(cosmo_import_artifact)
16 changes: 16 additions & 0 deletions cmake/feat-exception.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

# Disable C++ exceptions.
message(STATUS "Disabling C++ exceptions")
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if ("${CMAKE_CXX_FLAGS} " STREQUAL " ")
else ()
string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
endif ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
else (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if ("${CMAKE_CXX_FLAGS} " STREQUAL " ")
else ()
string(REGEX REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
endif ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
endif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
16 changes: 16 additions & 0 deletions cmake/feat-rtti.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

# Disable RTTI.
message(STATUS "Disabling C++ RTTI")
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if ("${CMAKE_CXX_FLAGS} " STREQUAL " ")
else ()
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
endif ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
else (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if ("${CMAKE_CXX_FLAGS} " STREQUAL " ")
else ()
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
endif ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
4 changes: 4 additions & 0 deletions cmake/feat-use-lld.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
3 changes: 3 additions & 0 deletions cmd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

add_executable(cosmoc cosmoc/main.cc)
target_link_libraries(cosmoc PUBLIC cosmo_std)
23 changes: 23 additions & 0 deletions cmd/cosmoc/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <cstdio>

#include <cosmo/std/src/fs.h>

#include "package-less/samples/Pattern/natAdd.cc"

using cosmo_std::str::String;

int main() {
auto file_content = cosmo_std::fs::readFile(String("cmd/CMakeLists.txt"));
printf("Read with cosmo function: Content of `cmd/CMakeLists.txt`\n");
printf("----------------\n");
printf("%s\n", file_content.internal.c_str());
printf("----------------\n");
Nat n = Nat::Zero_cons();
Nat one = Nat::Succ_cons(Nat::Zero_cons());
Nat x = n.clone();
for (int i = 0; i < 10; i++) {
x = Nat::Succ_cons(std::move(x));
}
printf("Nat(x).to_int() = %d\n", x.to_int());
return 0;
}
5 changes: 2 additions & 3 deletions packages/cosmo/src/main/scala/cosmo/CodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ class CodeGen(implicit val env: Env) {
val name = defInfo.nameStem(defInfo.id.id)
val ty = defInfo.instantiateTy
val p = s"std::move(${name}_p)"
if ty == SelfTy then s"$name(std::make_unique<Self>($p))"
if ty == SelfTy then s"$name(std::move(std::make_unique<Self>($p)))"
else s"$name($p)"
}

Expand Down Expand Up @@ -627,9 +627,8 @@ class CodeGen(implicit val env: Env) {
} else {
val xys = rhs.map(moveExpr);
debugln(s"literalCall: $lhs, $xys")
val (xs, ys) = (xys.map(_._1), xys.map(_._2));
val (xs, ys) = (xys.map(_._1).filter(_.nonEmpty), xys.map(_._2));
val call = s"$lhs(${ys.mkString(", ")})";
// s"${xs.mkString(";")}"
recv match {
case ValRecv.None => s"${xs.mkString(";")} $call"
case ValRecv.Return => s"${xs.mkString(";")} return $call"
Expand Down
2 changes: 1 addition & 1 deletion packages/cosmo/src/main/scala/cosmo/Cosmo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ trait Transpiler {
class Cosmo extends PackageManager, Transpiler {
var packages: Map[String, Map[String, Package]] = Map()
val system: CosmoSystem = new JsPhysicalSystem()
val linker: Linker = new MsvcLinker(system)
val linker: Linker = new CmakeLinker(system)
val envBase = new Env(None, this).builtins()

@JSExport
Expand Down
1 change: 1 addition & 0 deletions packages/cosmo/src/main/scala/cosmo/Eval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,7 @@ class Env(val fid: Option[FileId], pacMgr: cosmo.PackageManager) {
case l @ (Bool(_)) => builtinClasses(l.ty)
case l @ (Str(_)) => builtinClasses(l.ty)
case l @ (Integer(_)) => builtinClasses(l.ty)
case Unresolved(_) => Class.empty(this, false)
case _ => throw new Exception(s"cannot get class $lhs")
}
}
Expand Down
128 changes: 128 additions & 0 deletions packages/cosmo/src/main/scala/cosmo/linker/CmakeLinker.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package cosmo.linker

import cosmo.system._
import cosmo.{Package, Transpiler, FileId}
import cosmo.{debugln, logln}

import scala.scalajs.js

class CmakeLinker(system: CosmoSystem) extends Linker {
lazy val buildDir = "cmake-build-relwithdebinfo";

def writeIfDiff(path: String, content: String): Unit =
cosmo.linker.writeIfDiff(system, path, content)

def assemblePkg(
pkg: cosmo.Package,
t: cosmo.Transpiler,
relReleaseDir: String,
): Unit = {
val start = System.currentTimeMillis()
headOnlyPkg(
pkg,
t,
relReleaseDir,
writeIfDiff,
);

writeIfDiff(
s"$relReleaseDir/CMakeLists.txt",
s"""
include(packageOnly.cmake)

add_library(cosmo_std INTERFACE)
target_include_directories(cosmo_std INTERFACE .)
""",
);

writeIfDiff(
s"$relReleaseDir/packageOnly.cmake",
s"""

""",
);
debugln(s"Assembly time: ${System.currentTimeMillis() - start}ms")
}

def compile(
path: String,
t: cosmo.Transpiler,
relReleaseDir: String,
): Option[String] = {
val start = System.currentTimeMillis()
val src = system.readFile(path)
val generated = t.transpile(src).map { case (content, noCore) =>
var suf = if (noCore) "/lang" else ""
s"""#include <cosmo/std/src/prelude${suf}.h> // IWYU pragma: keep\n\n${content}"""
}

var nlJsonDir = system
.absPath("target/cosmo/externals/json/single_include")
.replace("\\", "\\\\")

var releaseDir =
system.absPath(relReleaseDir).replace("\\", "\\\\")

var includeFlags = js.Array(
s"/I$nlJsonDir",
s"/I$releaseDir",
)

val fileName = path.substring(0, path.length - 4)
val destDir = "package-less"
val destPath = destDir + "/" + fileName + ".cc"
val dirPath = destPath.substring(0, destPath.lastIndexOf("/"))

generated.flatMap { content =>
system.mkdir(releaseDir + "/" + dirPath)
system.writeFile(releaseDir + "/" + destPath, content)

writeIfDiff(
s"$relReleaseDir/packageOnly.cmake",
s"""
add_executable(cosmo-user-prog $destPath)
target_link_libraries(cosmo-user-prog PUBLIC cosmo_std)
""",
);

val target = "cosmo-user-prog"

val cmCommand = "cmake"
val cmArgs = js.Array(
"--build",
buildDir,
"--config",
"RelWithDebInfo",
"--target",
target,
)

val result = cosmo.NodeChildProcess.spawnSync(
cmCommand,
cmArgs,
js.Dynamic.literal(
encoding = "utf8",
stdio = "pipe",
// stdio = js.Tuple3("pipe", 2, "pipe"),
),
)

def programPath = {
// todo: this only works with ninja
Some(s"$buildDir/$relReleaseDir/$target.exe")
}

debugln(s"Compilation time: ${System.currentTimeMillis() - start}ms")
result.status.toOption match {
case Some(0) => programPath
case None => programPath
case Some(status) =>
println(result.stdout.toString().trim())
println(result.stderr.toString().trim())
println(result.error.toString().trim())
println(s"Compilation failed: cmake.exe Exit with status: ${status}")
None
}
}
}
}
51 changes: 51 additions & 0 deletions packages/cosmo/src/main/scala/cosmo/linker/HeadOnly.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cosmo.linker

import cosmo.system.CosmoSystem
import cosmo.{Package, Transpiler, FileId}

def headOnlyPkg(
pkg: Package,
t: Transpiler,
releaseDir: String,
write: (String, String) => Unit,
) = {
val destDir = releaseDir + "/" + pkg.namespace + "/" + pkg.name + "/src"
var sources = List[String]()
val identifier = (pkg.namespace + "_" + pkg.name).toUpperCase
for ((path, src) <- pkg.sources) {
if (!path.endsWith("staging.cos")) {
// .cos -> .cc, .h
var pathWithoutExt = path.substring(0, path.length - 4)
var ccPath = destDir + pathWithoutExt + ".cc"
var hPath = destDir + pathWithoutExt + ".h"
// println(s"Preloading $path => $hPath");

var fileName = pathWithoutExt.substring(
pathWithoutExt.lastIndexOf("/") + 1,
)

var subIdentifier =
(identifier + "_" + pathWithoutExt.replace("/", "_")).toUpperCase

var generated =
t.transpile(src.source, Some(src.fid)).map { case (content, noCore) =>
s"""#ifndef ${subIdentifier}_H\n#define ${subIdentifier}_H\n\n""" + content + s"\n\n#endif // ${subIdentifier}_H\n"
}

var dirPath = destDir + pathWithoutExt.substring(
0,
pathWithoutExt.lastIndexOf("/"),
)
generated.map(content =>
write(hPath, content)
write(
ccPath,
s"#include \"$fileName.h\" // IWYU pragma: keep\n",
),
)
sources = sources :+ ccPath
}
}

(destDir, sources)
}
Loading

0 comments on commit 8ae23cc

Please sign in to comment.