Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crude Tcl interpreter #18

Merged
merged 1 commit into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "third_party/tcl"]
path = third_party/tcl
url = https://github.com/tcltk/tcl
[submodule "third_party/zlib"]
path = third_party/zlib
url = https://github.com/madler/zlib
262 changes: 262 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
# -*- mode:cmake -*-
cmake_minimum_required(VERSION 3.15)

# Detect build type, fallback to release and throw a warning if use didn't
# specify any
if(NOT CMAKE_BUILD_TYPE)
message(WARNING "Build type not set, falling back to Release mode.
To specify build type use:
-DCMAKE_BUILD_TYPE=<mode> where <mode> is Debug or Release.")
set(CMAKE_BUILD_TYPE
"Release"
CACHE STRING "Choose the type of build, options are: Debug Release."
FORCE)
endif(NOT CMAKE_BUILD_TYPE)

option(
WITH_LIBCXX
"Building with clang++ and libc++(in Linux). To enable with: -DWITH_LIBCXX=On"
On)

project(FOEDAG)

# NOTE: Policy changes has to happen before adding any subprojects
cmake_policy(SET CMP0091 NEW)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_subdirectory(third_party/tcl_cmake EXCLUDE_FROM_ALL)
add_subdirectory(third_party/zlib EXCLUDE_FROM_ALL)

# NOTE: Set the global output directories after the subprojects have had their go at it
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)

set(GENDIR ${CMAKE_CURRENT_BINARY_DIR}/generated)

# Python
if (FOEDAG_WITH_PYTHON)
find_package(Python3 3.3 REQUIRED COMPONENTS Interpreter Development)
find_package(SWIG 3.0 REQUIRED)
message(STATUS "Python3_LIBRARIES = ${Python3_LIBRARIES}")
message(STATUS "Python3_EXECUTABLE = ${Python3_EXECUTABLE}")
message(STATUS "Python3_INCLUDE_DIRS = ${Python3_INCLUDE_DIRS}")
message(STATUS "Python3_RUNTIME_LIBRARY_DIRS = ${Python3_RUNTIME_LIBRARY_DIRS}")
endif()

if(NOT NO_TCMALLOC)
find_library(TCMALLOC_LIBRARY NAMES tcmalloc)
if(TCMALLOC_LIBRARY)
set(TCMALLOC_COMPILE_OPTIONS
"-fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free"
)
endif()
endif()

set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} ${TCMALLOC_COMPILE_OPTIONS} ${MY_CXX_WARNING_FLAGS}")

if(MSVC)
add_compile_definitions(_CRT_NONSTDC_NO_WARNINGS)

set(CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG} ${TCMALLOC_COMPILE_OPTIONS} /Zc:__cplusplus /W0 /bigobj ${MY_CXX_WARNING_FLAGS}"
)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
"${CMAKE_CXX_FLAGS_RELEASE} ${TCMALLOC_COMPILE_OPTIONS} /Zc:__cplusplus /W0 /bigobj ${MY_CXX_WARNING_FLAGS}"
)
set(CMAKE_CXX_FLAGS_RELEASE
"${CMAKE_CXX_FLAGS_RELEASE} ${TCMALLOC_COMPILE_OPTIONS} /Zc:__cplusplus /W0 /bigobj ${MY_CXX_WARNING_FLAGS}"
)
set(CMAKE_EXE_LINKER_FLAGS /STACK:8388608) # 8MB stack size
else()
if(DEFINED ENV{MSYSTEM})
# Under MSYS some files are too large to build without additional flags
set(MSYS_COMPILE_OPTIONS "-m64 -Wa,-mbig-obj")
endif()
#set(MEM_SANITIZER_FLAGS
# "-fsanitize=address -fno-omit-frame-pointer"
#)
set(CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG} ${TCMALLOC_COMPILE_OPTIONS} -Wall -O0 -g ${MSYS_COMPILE_OPTIONS} ${MY_CXX_WARNING_FLAGS} ${MEM_SANITIZER_FLAGS}"
)
set(CMAKE_CXX_FLAGS_RELEASE
"${CMAKE_CXX_FLAGS_RELEASE} ${TCMALLOC_COMPILE_OPTIONS} -Wall -O3 ${MSYS_COMPILE_OPTIONS} -DNDEBUG ${MY_CXX_WARNING_FLAGS}"
)
endif()

include_directories(${PROJECT_SOURCE_DIR}/src ${GENDIR}/src third_party/build/include/)

# Put source code here, files that are generated at build time in
# foedag_generated_SRC
set(foedag_SRC
${PROJECT_SOURCE_DIR}/src/Main/main.cpp
)

add_library(foedag STATIC ${foedag_SRC})
set_target_properties(foedag PROPERTIES PUBLIC_HEADER src/foedag.h)
target_include_directories(foedag PRIVATE
third_party/tcl/include)
target_include_directories(foedag PUBLIC $<INSTALL_INTERFACE:include/foedag>)

add_executable(foedag-bin ${PROJECT_SOURCE_DIR}/src/Main/main.cpp)
set_target_properties(foedag-bin PROPERTIES OUTPUT_NAME foedag)

if (APPLE)
# In macOS, it is necessary to add the correct @rpath to the executable for
# finding python dynamic libraries ref: https://gitlab.kitware.com/cmake/cmake/-/issues/21293
# https://gitlab.kitware.com/cmake/cmake/-/issues/21947
# Python3_LINK_OPTIONS is variable available from cmake 3.19, update cmake using homebrew
# if can't update cmake use:
# set_target_properties(foedag-bin PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE
# INSTALL_RPATH "/Library/Developer/CommandLineTools/Library/Frameworks/")
# if you installed python with hombrew. Or if you install python with Xcode:
# set_target_properties(foedag-bin PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE
# INSTALL_RPATH "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/")
endif()

if(MSVC OR WIN32)
# We have two files named "foedag.lib" and both getting generated in the lib folder
# One is the foedag.lib generated by the foedag target and the other is the one generated
# becaues of /IMPLIB option when linking the executable. Unfortunately, there is no documented
# way to disable the latter in CMake. So, moving the library to the bin directory (right next to the exe)
set_target_properties(foedag-bin PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
endif()



add_custom_command(
TARGET foedag
# OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/third_party/build/lib/libtcl9.0.a
COMMAND echo " Compiling Tcl"
COMMAND ./configure --enable-shared=off --prefix=${PWD}/../../../build
COMMAND make
COMMAND echo " Tcl Compilation completed"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/third_party/tcl/unix"
)


ADD_LIBRARY(tcl_static STATIC IMPORTED)
SET_TARGET_PROPERTIES(tcl_static PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/third_party/tcl/unix/libtcl9.0.a)


target_link_libraries(foedag-bin PUBLIC foedag tcl_static)
target_link_libraries(foedag PUBLIC tcl_static zlib)

if(NOT NO_TCMALLOC)
find_library(TCMALLOC_LIBRARY NAMES tcmalloc)
if(TCMALLOC_LIBRARY)
target_link_libraries(foedag PRIVATE tcmalloc)
endif()
endif()

if (UNIX)
target_link_libraries(foedag PRIVATE dl)
target_link_libraries(foedag PRIVATE util)
target_link_libraries(foedag PRIVATE m)
target_link_libraries(foedag PRIVATE pthread)
endif()

if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_link_libraries(foedag PRIVATE stdc++fs)
target_link_libraries(foedag PRIVATE rt)
endif()

# Unit tests
enable_testing()
include(GoogleTest)

if(MSVC)
# Microsoft reports the value of __cplusplus wrong and gmock/gtest pulls in the
# string_view implementation based on it's value. Microsoft's solution is to
# provide additional flags to make the value correct. More info can be found here -
#
# https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-160
# https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
target_compile_options(gmock PRIVATE /Zc:__cplusplus)
target_compile_options(gmock_main PRIVATE /Zc:__cplusplus)
target_compile_options(gtest PRIVATE /Zc:__cplusplus)
target_compile_options(gtest_main PRIVATE /Zc:__cplusplus)
endif()

# All unit-tests are registered with this custom target as dependency, so
# just `make UnitTests` will build them all.
add_custom_target(UnitTests)

# Concise way to register a new google test
# call with register_gtest(path/to/UnitTestFile.cpp)
function(register_gtests)
foreach(gtest_cc_file IN LISTS ARGN)
# We create the binary name and test prefix from the cpp-filepath
get_filename_component(test_bin ${gtest_cc_file} NAME_WE)
get_filename_component(test_prefix ${gtest_cc_file} DIRECTORY)

# Build binary, link all relevant libs and extract tests
add_executable(${test_bin} EXCLUDE_FROM_ALL ${gtest_cc_file})

target_include_directories(${test_bin} PRIVATE
)
# For simplicity, we link the test with libfoedag, but there is of
# course a lot unnecessary churn if headers are modified.
# Often it is sufficient to just have a few depeendencies.
target_link_libraries(${test_bin} foedag gtest gmock gtest_main)
gtest_discover_tests(${test_bin} TEST_PREFIX "${test_prefix}/")

# Now, add this binary to our UnitTests target that it builds this
add_dependencies(UnitTests ${test_bin})
endforeach()
endfunction()

register_gtests(
)

# Installation target
install(
TARGETS foedag-bin
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
#install(
# TARGETS foedag tcl_static
# EXPORT Foedag
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/foedag
# PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/foedag)

if (WIN32 AND $<CONFIG:Debug>)
if (FOEDAG_WITH_PYTHON)
install(
FILES $<TARGET_PDB_FILE:foedag-bin>
${Python3_RUNTIME_LIBRARY_DIRS}/python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}$<$<CONFIG:Debug>:_d>.dll
DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/foedag.dir/foedag.pdb
${TCL_BINARY_DIR}/runtime/CMakeFiles/tcl_static.dir/tcl_static.pdb
DESTINATION ${CMAKE_INSTALL_LIBDIR}/foedag)
endif()

#install(
# EXPORT Foedag
# FILE Foedag.cmake
# DESTINATION cmake)
include(CMakePackageConfigHelpers)

# generate the config file that is includes the exports
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/FoedagConfig.cmake"
INSTALL_DESTINATION cmake
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO)

# install the configuration file
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/FoedagConfig.cmake
DESTINATION cmake)

ADD_CUSTOM_TARGET(link_target ALL
COMMAND ${CMAKE_COMMAND} -E create_symlink
build/compile_commands.json ../compile_commands.json)
3 changes: 3 additions & 0 deletions Config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@PACKAGE_INIT@

include ( "${CMAKE_CURRENT_LIST_DIR}/Foedag.cmake" )
82 changes: 82 additions & 0 deletions src/Main/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright 2021 The Foedag team

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#if defined(_MSC_VER)
#include <direct.h>
#include <process.h>
#else
#include <sys/param.h>
#include <unistd.h>
#endif

#include <string.h>
#include <sys/stat.h>

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include <stdlib.h>
#include <tcl.h>

class TclInterpreter {
private:
Tcl_Interp *interp;
public:
TclInterpreter(const char *argv0 = nullptr) : interp(nullptr) {
static bool initLib;
if (!initLib) {
Tcl_FindExecutable(argv0);
initLib = true;
}
interp = Tcl_CreateInterp();
if (!interp) throw new std::runtime_error("failed to initialise Tcl library");
}

~TclInterpreter() {
if (interp) Tcl_DeleteInterp(interp);
}

std::string evalFile(const std::string &filename) {
int code = Tcl_EvalFile(interp, filename.c_str());

if (code >= TCL_ERROR) {

throw Tcl_GetStringResult(interp);
}
return std::string(Tcl_GetStringResult(interp));
}

std::string evalCmd(const std::string cmd) {
int code = Tcl_Eval(interp, cmd.c_str());

if (code >= TCL_ERROR) {

throw Tcl_GetStringResult(interp);
}
return std::string(Tcl_GetStringResult(interp));
}
};


int main(int argc, const char** argv) {

TclInterpreter interpreter(argv[0]);
std::string result = interpreter.evalCmd("puts \"Hello Foedag, you have Tcl!\"");
std::cout << result << '\n';

}
Loading