diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab4fe860..0b3fa91f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,10 @@ name: CI on: [push, pull_request] +defaults: + run: + shell: bash + jobs: build-linux: runs-on: ubuntu-latest @@ -10,6 +14,9 @@ jobs: image: centos/devtoolset-7-toolchain-centos7 options: --user 0 steps: + - name: Install Git + run: yum install -y git + - name: Install CMake env: version: 3.17.3 @@ -25,29 +32,21 @@ jobs: - name: Get sources uses: actions/checkout@v2 - - name: Cache FBS-C-Bridge build dir - id: fbs-bridge-cache + - name: Cache Deps uses: actions/cache@v2 - env: - cache-name: cache-fbs-c-bridge-build with: - path: ./third_party/flatbuffers-c-bridge/cmake-build/*.a - key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('./third_party/flatbuffers-c-bridge/*') }} + path: ./third_party/* + key: ${{ runner.os }}-deps-${{ hashFiles('./third_party/*') }} - - name: Build FBS-C-Bridge - if: steps.fbs-bridge-cache.outputs.cache-hit != 'true' - run: ./third_party/flatbuffers-c-bridge/build.sh - - - name: Build - run: | - cd cmd/objectbox-generator - go build + - run: make + - run: make test-depend + - run: make test - name: Upload artifact uses: actions/upload-artifact@v2 with: name: objectbox-generator-${{ runner.os }} - path: cmd/objectbox-generator/objectbox-generator + path: objectbox-generator build: strategy: @@ -57,30 +56,23 @@ jobs: - macos-10.15 runs-on: ${{ matrix.os }} steps: + - run: git config --global core.autocrlf false + - name: Get sources uses: actions/checkout@v2 - - name: Cache FBS-C-Bridge build dir - id: fbs-bridge-cache + - name: Cache Deps uses: actions/cache@v2 - env: - cache-name: cache-fbs-c-bridge-build with: - path: ./third_party/flatbuffers-c-bridge/cmake-build/*.a - key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('./third_party/flatbuffers-c-bridge/*') }} + path: ./third_party/* + key: ${{ runner.os }}-deps-${{ hashFiles('./third_party/*') }} - - name: Build FBS-C-Bridge - if: steps.fbs-bridge-cache.outputs.cache-hit != 'true' - shell: bash - run: ./third_party/flatbuffers-c-bridge/build.sh - - - name: Build - run: | - cd cmd/objectbox-generator - go build + - run: make + - run: make test-depend + - run: make test - name: Upload artifact uses: actions/upload-artifact@v2 with: name: objectbox-generator-${{ runner.os }} - path: cmd/objectbox-generator/objectbox-generator* + path: objectbox-generator* diff --git a/Makefile b/Makefile index 154164a4..4ee3d369 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,11 @@ # Default target executed when no arguments are given to make. default_target: all -.PHONY: default_target help clean depend build test +.PHONY: default_target help clean depend build test test-depend help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - - #============================================================================== all: depend build @@ -23,6 +21,13 @@ clean: ## Clean previous builds rm -f objectbox-generator rm -f objectbox-generator.exe rm -rf third_party/flatbuffers-c-bridge/cmake-build + ./third_party/flatcc/clean.sh + ./third_party/objectbox-c/clean.sh depend: ## Build dependencies ./third_party/flatbuffers-c-bridge/build.sh + +test-depend: depend ## Build test dependencies + ./third_party/flatcc/build.sh + ./third_party/objectbox-c/get-objectbox-c.sh + diff --git a/internal/generator/flatbuffersc/fbsc_test.go b/internal/generator/flatbuffersc/fbsc_test.go index 31f10a65..1d3b724d 100644 --- a/internal/generator/flatbuffersc/fbsc_test.go +++ b/internal/generator/flatbuffersc/fbsc_test.go @@ -65,6 +65,7 @@ func TestFbsSchemaParser(t *testing.T) { _, err = file.WriteString(testSchema) assert.NoErr(t, err) + assert.NoErr(t, file.Close()) schema, err = ParseSchemaFile(file.Name()) assert.NoErr(t, err) @@ -123,6 +124,7 @@ func TestFbsFlatc(t *testing.T) { _, err = file.WriteString(testSchema) assert.NoErr(t, err) + assert.NoErr(t, file.Close()) code, err = ExecuteFlatc([]string{"--go", "-o", outDir, file.Name()}) assert.NoErr(t, err) diff --git a/test/build/c-compiler.go b/test/build/c-compiler.go new file mode 100644 index 00000000..89706c90 --- /dev/null +++ b/test/build/c-compiler.go @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 ObjectBox Ltd. All rights reserved. + * https://objectbox.io + * + * This file is part of ObjectBox Generator. + * + * ObjectBox Generator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * ObjectBox Generator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ObjectBox Generator. If not, see . + */ + +package build + +import ( + "testing" + + "github.com/objectbox/objectbox-generator/test/assert" + "github.com/objectbox/objectbox-generator/test/cmake" +) + +// Check verifies the C/C++ objectbox test code can be compiled - whether the required libraries are available. +func CanCompileObjectBoxCCpp(t *testing.T, repoRoot string, cpp, required bool) bool { + var err error + + var includeDirs = IncludeDirs(repoRoot) + var libDirs = LibDirs(repoRoot) + + // check objectbox lib + if cpp { + err = cmake.LibraryExists("objectbox", []string{"objectbox-cpp.h"}, includeDirs, libDirs) + } else { + err = cmake.LibraryExists("objectbox", []string{"objectbox.h"}, includeDirs, libDirs) + } + assert.NoErr(t, err) + + // check flatbuffers library availability + if cpp { + // Note: we don't need flatbuffers library explicitly, it's part of objectbox at the moment. + err = cmake.LibraryExists("", []string{"flatbuffers/flatbuffers.h"}, includeDirs, libDirs) + } else { + err = cmake.LibraryExists("flatccrt", []string{"stddef.h", "flatcc/flatcc.h", "flatcc/flatcc_builder.h"}, includeDirs, libDirs) + } + + if required { + assert.NoErr(t, err) + } else if err != nil { + t.Logf("C/C++ compilation not available because %s", err) + return false + } + return true +} diff --git a/test/build/conf.go b/test/build/conf.go new file mode 100644 index 00000000..8b627126 --- /dev/null +++ b/test/build/conf.go @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 ObjectBox Ltd. All rights reserved. + * https://objectbox.io + * + * This file is part of ObjectBox Generator. + * + * ObjectBox Generator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * ObjectBox Generator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ObjectBox Generator. If not, see . + */ + +package build + +import "path" + +const flatbuffersDir = "third_party/flatbuffers-c-bridge/third_party/flatbuffers" +const flatccDir = "third_party/flatcc" +const objectBoxCDir = "third_party/objectbox-c" + +func IncludeDirs(repoRoot string) []string { + var result []string + result = append(result, path.Join(repoRoot, flatbuffersDir, "include")) + result = append(result, path.Join(repoRoot, flatccDir, "include")) + result = append(result, path.Join(repoRoot, objectBoxCDir, "include")) + return result +} + +func LibDirs(repoRoot string) []string { + var result []string + result = append(result, path.Join(repoRoot, objectBoxCDir, "lib")) + result = append(result, path.Join(repoRoot, flatccDir, "lib")) + return result +} diff --git a/test/cmake/cmake.go b/test/cmake/cmake.go new file mode 100644 index 00000000..1770b244 --- /dev/null +++ b/test/cmake/cmake.go @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2020 ObjectBox Ltd. All rights reserved. + * https://objectbox.io + * + * This file is part of ObjectBox Generator. + * + * ObjectBox Generator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * ObjectBox Generator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ObjectBox Generator. If not, see . + */ + +// package cmake provides tools to create, configure and build C & C++ projects using CMake +package cmake + +import ( + "bufio" + "bytes" + "errors" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "text/template" +) + +// Cmake contains all configuration necessary to configure and build a CMake project +type Cmake struct { + // Configs required for CMakeLists.txt + Name string // Executable name + IsCpp bool + Standard int // CMAKE_C/CXX_STANDARD + Files []string + IncludeDirs []string + LinkLibs []string // Library names or full paths + LinkDirs []string // Where should the linker look for libraries + Generator string + + // Build configuration + SourceDir string + ConfDir string + BuildDir string + + tempRoot string +} + +// CreateTempDirs creates temporary directories for conf and build dir +func (cmake *Cmake) CreateTempDirs() error { + if len(cmake.tempRoot) != 0 { + return errors.New("temp root is already set") + } + + tempRoot, err := ioutil.TempDir("", cmake.Name+"cmake") + if err != nil { + return err + } + + buildDir, err := createTempDir(tempRoot, "build") + confDir, err2 := createTempDir(tempRoot, "conf") + + if err != nil || err2 != nil { + os.RemoveAll(cmake.tempRoot) + if err != nil { + return err + } + return err2 + } + + cmake.tempRoot = tempRoot + cmake.BuildDir = buildDir + cmake.ConfDir = confDir + + if len(cmake.SourceDir) == 0 { + cmake.SourceDir = cmake.ConfDir + } + return nil +} + +func createTempDir(parent, name string) (string, error) { + var path = filepath.Join(parent, name) + if err := os.Mkdir(path, 0700); err != nil { + return "", err + } + return path, nil +} + +func (cmake *Cmake) RemoveTempDirs() error { + if len(cmake.SourceDir) == 0 { + cmake.SourceDir = cmake.ConfDir + } + return os.RemoveAll(cmake.tempRoot) +} + +// WriteCMakeListsTxt writes cmake specification to file and returns the file name. +func (cmake *Cmake) WriteCMakeListsTxt() error { + // open the file, overwrite if it exists + file, err := os.OpenFile(filepath.Join(cmake.ConfDir, "CMakeLists.txt"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + defer file.Close() + + writer := bufio.NewWriter(file) + if err := cmakeListsTpl.Execute(writer, cmake); err != nil { + return err + } + + return writer.Flush() +} + +func (cmake *Cmake) GetCMakeListsTxt() (string, error) { + var b bytes.Buffer + writer := bufio.NewWriter(&b) + if err := cmakeListsTpl.Execute(writer, cmake); err != nil { + return "", err + } else if err = writer.Flush(); err != nil { + return "", err + } + return string(b.Bytes()), nil +} + +var cmakeListsTpl = template.Must(template.New("CMakeLists.txt"). + Funcs(template.FuncMap{ + "Join": func(ss []string) string { + switch len(ss) { + case 0: + return "" + case 1: + return filepath.ToSlash(ss[0]) + default: + var result string + for _, s := range ss { + result = result + "\n\t" + filepath.ToSlash(s) + } + return result + } + }, + }). + Parse(` +cmake_minimum_required(VERSION 3.0) +{{if .Standard}}set(CMAKE_C{{if .IsCpp}}XX{{end}}_STANDARD {{.Standard}}){{end}} +project({{.Name}} C{{if .IsCpp}}XX{{end}}) + +add_executable(${PROJECT_NAME} {{Join .Files}}) +{{if .IncludeDirs}}target_include_directories(${PROJECT_NAME} PRIVATE {{Join .IncludeDirs}}){{end}} +{{if .LinkLibs}}target_link_libraries(${PROJECT_NAME} PRIVATE {{Join .LinkLibs}}){{end}} +{{if .LinkDirs}}target_link_directories(${PROJECT_NAME} PRIVATE {{Join .LinkDirs}}){{end}} +`)) + +// Configure runs cmake configuration step. +func (cmake *Cmake) Configure() ([]byte, []byte, error) { + if len(cmake.Generator) == 0 && runtime.GOOS == "windows" { + // Using MinGW because MSVC doesn't support linking to .dll - app is supposed to load them on runtime + cmake.Generator = "MinGW Makefiles" + } + + if len(cmake.Generator) > 0 { + return cmakeExec(cmake.BuildDir, cmake.ConfDir, "-G", cmake.Generator) + } else { + return cmakeExec(cmake.BuildDir, cmake.ConfDir) + } +} + +// Configure runs cmake build step. +func (cmake *Cmake) Build() ([]byte, []byte, error) { + return cmakeExec(cmake.SourceDir, "--build", cmake.BuildDir) +} + +func cmakeExec(cwd string, args ...string) (stdOut []byte, stdErr []byte, err error) { + var cmd = exec.Command("cmake", args...) + cmd.Dir = cwd + stdOut, err = cmd.Output() + if ee, ok := err.(*exec.ExitError); ok { + stdErr = ee.Stderr + } + return +} diff --git a/test/cmake/libcheck.go b/test/cmake/libcheck.go new file mode 100644 index 00000000..1c3b6231 --- /dev/null +++ b/test/cmake/libcheck.go @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 ObjectBox Ltd. All rights reserved. + * https://objectbox.io + * + * This file is part of ObjectBox Generator. + * + * ObjectBox Generator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * ObjectBox Generator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ObjectBox Generator. If not, see . + */ + +package cmake + +import ( + "fmt" + "io/ioutil" + "path/filepath" +) + +// LibraryExists tries to compile a simple program linking to the given library +func LibraryExists(name string, includeFiles, includeDirs, linkDirs []string) error { + build := Cmake{ + Name: "check-" + name, + IsCpp: true, + Standard: 11, + Files: []string{"main.cpp"}, + IncludeDirs: includeDirs, + LinkDirs: linkDirs, + } + if err := build.CreateTempDirs(); err != nil { + return err + } + defer build.RemoveTempDirs() + + if len(name) > 0 { + build.LinkLibs = []string{name} + } + + if err := build.WriteCMakeListsTxt(); err != nil { + return err + } + + { // write main.cpp + mainPath := filepath.Join(build.SourceDir, build.Files[0]) + var mainSrc string + if len(includeFiles) > 0 { + for _, inc := range includeFiles { + mainSrc = mainSrc + "#include <" + inc + ">\n" + } + } + mainSrc = mainSrc + "\nint main(){ return 0; }\n\n" + if err := ioutil.WriteFile(mainPath, []byte(mainSrc), 0600); err != nil { + return err + } + } + + if stdOut, stdErr, err := build.Configure(); err != nil { + return fmt.Errorf("cmake configuration failed: \n%s\n%s\n%s", stdOut, stdErr, err) + } + + if stdOut, stdErr, err := build.Build(); err != nil { + return fmt.Errorf("cmake build failed: \n%s\n%s\n%s", stdOut, stdErr, err) + } + + return nil +} diff --git a/test/cmake/libcheck_test.go b/test/cmake/libcheck_test.go new file mode 100644 index 00000000..f9402609 --- /dev/null +++ b/test/cmake/libcheck_test.go @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 ObjectBox Ltd. All rights reserved. + * https://objectbox.io + * + * This file is part of ObjectBox Generator. + * + * ObjectBox Generator is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * ObjectBox Generator is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ObjectBox Generator. If not, see . + */ + +package cmake_test + +import ( + "runtime" + "testing" + + "github.com/objectbox/objectbox-generator/test/assert" + "github.com/objectbox/objectbox-generator/test/cmake" +) + +func TestLibExists(t *testing.T) { + if testing.Short() { + t.SkipNow() + } + + err := cmake.LibraryExists("nonsense", nil, nil, nil) + assert.Err(t, err) + + err = cmake.LibraryExists("", []string{"non-existent-lib/include.h"}, nil, nil) + assert.Err(t, err) + + if runtime.GOOS == "windows" { + err = cmake.LibraryExists("", []string{"array"}, nil, nil) + } else { + err = cmake.LibraryExists("stdc++", []string{"array"}, nil, nil) + } + assert.NoErr(t, err) +} diff --git a/test/comparison/c-helper.go b/test/comparison/c-helper.go index efeb87d7..6404ea11 100644 --- a/test/comparison/c-helper.go +++ b/test/comparison/c-helper.go @@ -20,37 +20,34 @@ package comparison import ( - "bufio" - "bytes" "io/ioutil" "os" - "os/exec" "path" "path/filepath" "testing" - "text/template" "github.com/objectbox/objectbox-generator/internal/generator" - cgenerator "github.com/objectbox/objectbox-generator/internal/generator/c" + "github.com/objectbox/objectbox-generator/internal/generator/c" "github.com/objectbox/objectbox-generator/test/assert" + "github.com/objectbox/objectbox-generator/test/build" + "github.com/objectbox/objectbox-generator/test/cmake" ) -var cmakeListsTpl = template.Must(template.New("cmake").Parse(` -cmake_minimum_required(VERSION 3.0) -{{if .Cpp}} -set(CMAKE_CXX_STANDARD 11) -{{else}} -set(CMAKE_C_STANDARD 99) -{{end}} -project(compilation-test {{if not .Cpp}}C{{end}}) +type cTestHelper struct { + cpp bool + canCompile bool +} -add_executable(${PROJECT_NAME} {{.Main}}) -target_include_directories(${PROJECT_NAME} PRIVATE {{.Include}}) -target_link_libraries(${PROJECT_NAME} objectbox) -`)) +func (h *cTestHelper) init(t *testing.T, conf testSpec) { + if !testing.Short() { + var mandatory = h.cpp // we require cpp compilation to be available at the moment + if _, isCI := os.LookupEnv("CI"); isCI { + t.Log("CI environment variable defined. Compilation support is mandatory.") + mandatory = true + } -type cTestHelper struct { - cpp bool + h.canCompile = build.CanCompileObjectBoxCCpp(t, repoRoot(t), h.cpp, mandatory) + } } func (h cTestHelper) generatorFor(t *testing.T, conf testSpec, sourceFile string, genDir string) generator.CodeGenerator { @@ -65,40 +62,49 @@ func (cTestHelper) prepareTempDir(t *testing.T, conf testSpec, srcDir, tempDir, } func (h cTestHelper) build(t *testing.T, conf testSpec, dir string, expectedError error, errorTransformer func(err error) error) { - includeDir, err := filepath.Abs(dir) // main.c/cpp will include generated headers from here - assert.NoErr(t, err) + if !h.canCompile { + t.Skip("Compilation not available") + } - tempRoot, err := ioutil.TempDir("", "objectbox-generator-test-build") + includeDir, err := filepath.Abs(dir) // main.c/cpp will include generated headers from here assert.NoErr(t, err) - defer os.RemoveAll(tempRoot) - buildDir := path.Join(tempRoot, "build") - cmakeConfDir := path.Join(tempRoot, "conf") // using "conf" dir to write CMakeLists.txt and main.c/cpp - assert.NoErr(t, os.Mkdir(buildDir, 0700)) - assert.NoErr(t, os.Mkdir(cmakeConfDir, 0700)) - - mainFile := path.Join(cmakeConfDir, "main.c") - if h.cpp { - mainFile = path.Join(cmakeConfDir, "main.cpp") + cmak := cmake.Cmake{ + Name: "compilation-test", + IsCpp: h.cpp, + IncludeDirs: append(build.IncludeDirs(repoRoot(t)), includeDir), + LinkDirs: build.LibDirs(repoRoot(t)), + LinkLibs: []string{"objectbox"}, + } + assert.NoErr(t, cmak.CreateTempDirs()) + defer cmak.RemoveTempDirs() + + var mainFile string + if cmak.IsCpp { + cmak.Standard = 11 + mainFile = path.Join(cmak.ConfDir, "main.cpp") + } else { + cmak.Standard = 99 + mainFile = path.Join(cmak.ConfDir, "main.c") + cmak.LinkLibs = append(cmak.LinkLibs, "flatccrt") } - { // write CMakeLists.txt to the conf dir - var tplArguments = struct { - Cpp bool - Ext string - Include string - Main string - }{h.cpp, conf.generatedExt, includeDir, mainFile} - - var b bytes.Buffer - writer := bufio.NewWriter(&b) - assert.NoErr(t, cmakeListsTpl.Execute(writer, tplArguments)) - assert.NoErr(t, writer.Flush()) - assert.NoErr(t, ioutil.WriteFile(path.Join(cmakeConfDir, "CMakeLists.txt"), b.Bytes(), 0600)) + cmak.Files = append(cmak.Files, mainFile) + + assert.NoErr(t, cmak.WriteCMakeListsTxt()) + if testing.Verbose() { + cml, err := cmak.GetCMakeListsTxt() + assert.NoErr(t, err) + t.Logf("Using CMakeLists.txt: %s", cml) } { // write main.c/cpp to the conf dir - a simple one, just include all sources var mainSrc = "" + if cmak.IsCpp { + mainSrc = mainSrc + "#include \"objectbox-cpp.h\"\n" + } else { + mainSrc = mainSrc + "#include \"objectbox.h\"\n" + } files, err := ioutil.ReadDir(includeDir) assert.NoErr(t, err) @@ -107,35 +113,22 @@ func (h cTestHelper) build(t *testing.T, conf testSpec, dir string, expectedErro mainSrc = mainSrc + "#include \"" + file.Name() + "\"\n" } } + mainSrc = mainSrc + "int main(){ return 0; }\n\n" + t.Logf("main.c/cpp file contents \n%s", mainSrc) assert.NoErr(t, ioutil.WriteFile(mainFile, []byte(mainSrc), 0600)) } - // configure the cmake project - stdOut, stdErr, err := cmake(buildDir, cmakeConfDir) - if err != nil { - assert.Failf(t, "cmake build configuration failed: \n%s\n%s\n%s", stdOut, stdErr, err) - } - if testing.Verbose() { + if stdOut, stdErr, err := cmak.Configure(); err != nil { + assert.Failf(t, "cmake configuration failed: \n%s\n%s\n%s", stdOut, stdErr, err) + } else { t.Logf("configuration output:\n%s", string(stdOut)) } - // build the code - stdOut, stdErr, err = cmake(includeDir, "--build", buildDir) - - checkBuildError(t, errorTransformer, stdOut, stdErr, err, expectedError) - - if testing.Verbose() { + if stdOut, stdErr, err := cmak.Build(); err != nil { + checkBuildError(t, errorTransformer, stdOut, stdErr, err, expectedError) + assert.Failf(t, "cmake build failed: \n%s\n%s\n%s", stdOut, stdErr, err) + } else { t.Logf("build output:\n%s", string(stdOut)) } } - -func cmake(cwd string, args ...string) (stdOut []byte, stdErr []byte, err error) { - var cmd = exec.Command("cmake", args...) - cmd.Dir = cwd - stdOut, err = cmd.Output() - if ee, ok := err.(*exec.ExitError); ok { - stdErr = ee.Stderr - } - return -} diff --git a/test/comparison/conf.go b/test/comparison/conf.go index b0e73612..7ac142da 100644 --- a/test/comparison/conf.go +++ b/test/comparison/conf.go @@ -27,6 +27,9 @@ import ( ) type testHelper interface { + // init sets up the helper before the very first execution + init(t *testing.T, conf testSpec) + // generatorFor constructs and configures a code generator for the given source file generatorFor(t *testing.T, conf testSpec, sourceFile string, genDir string) generator.CodeGenerator @@ -48,7 +51,7 @@ type testSpec struct { } var confs = map[string]testSpec{ - "fbs-c": {"c", ".fbs", ".obx.h", &cgenerator.CGenerator{PlainC: true}, cTestHelper{cpp: false}}, - "fbs-cpp": {"cpp", ".fbs", "-cpp.obx.h", &cgenerator.CGenerator{PlainC: false}, cTestHelper{cpp: true}}, + "fbs-c": {"c", ".fbs", ".obx.h", &cgenerator.CGenerator{PlainC: true}, &cTestHelper{cpp: false}}, + "fbs-cpp": {"cpp", ".fbs", "-cpp.obx.h", &cgenerator.CGenerator{PlainC: false}, &cTestHelper{cpp: true}}, // TODO "go": {"go", ".go", ".obx.go", &gogenerator.GoGenerator{}, goTestHelper{}}, } diff --git a/test/comparison/generator_test.go b/test/comparison/generator_test.go index db5742e8..3cb3c9b5 100644 --- a/test/comparison/generator_test.go +++ b/test/comparison/generator_test.go @@ -46,6 +46,7 @@ func TestCompare(t *testing.T) { srcType, genType := typesFromConfKey(parts[0]) conf, ok := confs[parts[0]] assert.True(t, ok) + conf.helper.init(t, conf) generateOneDir(t, *overwriteExpected, conf, srcType, genType, parts[1]) } else { t.Fatal("invalid target specification, expected 1 or two parts separated by '/'") diff --git a/test/comparison/go-helper.go b/test/comparison/go-helper.go index 56c8c44c..7e97d7ce 100644 --- a/test/comparison/go-helper.go +++ b/test/comparison/go-helper.go @@ -42,6 +42,8 @@ var goGeneratorArgsRegexp = regexp.MustCompile("//go:generate go run github.com/ type goTestHelper struct{} +func (h *goTestHelper) init(t *testing.T, conf testSpec) {} + func (h goTestHelper) generatorFor(t *testing.T, conf testSpec, sourceFile string, genDir string) generator.CodeGenerator { source, err := ioutil.ReadFile(sourceFile) assert.NoErr(t, err) diff --git a/test/comparison/test-all.go b/test/comparison/test-all.go index 238d60d2..562e6c44 100644 --- a/test/comparison/test-all.go +++ b/test/comparison/test-all.go @@ -53,6 +53,7 @@ func generateAllDirs(t *testing.T, overwriteExpected bool, confKey string) { conf, ok := confs[confKey] assert.True(t, ok) + conf.helper.init(t, conf) for _, testCase := range testCases { if !testCase.IsDir() { diff --git a/test/comparison/util.go b/test/comparison/util.go index 613910f4..eedda5f7 100644 --- a/test/comparison/util.go +++ b/test/comparison/util.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "strings" "testing" @@ -40,7 +41,7 @@ func checkBuildError(t *testing.T, errorTransformer func(err error) error, stdOu var receivedError = errorTransformer(fmt.Errorf("%s\n%s\n%s", stdOut, stdErr, err)) // Fix paths in the error output on Windows so that it matches the expected error (which always uses '/'). - if os.PathSeparator != '/' { + if os.PathSeparator != '/' && expectedError != nil { // Make sure the expected error doesn't contain the path separator already - to make it easier to debug. if strings.Contains(expectedError.Error(), string(os.PathSeparator)) { assert.Failf(t, "compile-error.expected contains this OS path separator '%v' so paths can't be normalized to '/'", string(os.PathSeparator)) @@ -50,3 +51,9 @@ func checkBuildError(t *testing.T, errorTransformer func(err error) error, stdOu assert.Eq(t, expectedError, receivedError) } + +func repoRoot(t *testing.T) string { + cwd, err := os.Getwd() + assert.NoErr(t, err) + return filepath.ToSlash(filepath.Join(cwd, "..", "..")) +} diff --git a/third_party/flatbuffers-c-bridge/build.sh b/third_party/flatbuffers-c-bridge/build.sh index 5c3196e3..3cc5843b 100755 --- a/third_party/flatbuffers-c-bridge/build.sh +++ b/third_party/flatbuffers-c-bridge/build.sh @@ -31,7 +31,15 @@ fi function build() { echo "******** Configuring & building ********" - srcDirAbsolute="$(pwd)/$srcDir" + # Note: we need an absolute path... + # realpath isn't available on macOS and the "else" variant didn't work well on windows because the path was already absolute... + srcDirAbsolute= + if [[ -x $(command -v realpath) ]]; then + srcDirAbsolute=$(realpath "$srcDir") + else + srcDirAbsolute="$(pwd)/$srcDir" + fi + pwd=$(pwd) mkdir -p "$buildDir" diff --git a/third_party/flatcc/.gitignore b/third_party/flatcc/.gitignore new file mode 100644 index 00000000..0e1dfdcc --- /dev/null +++ b/third_party/flatcc/.gitignore @@ -0,0 +1,4 @@ +build +src +lib +include \ No newline at end of file diff --git a/third_party/flatcc/build.sh b/third_party/flatcc/build.sh new file mode 100755 index 00000000..fe96056a --- /dev/null +++ b/third_party/flatcc/build.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -euo pipefail + +fccVersion=v0.6.0 +fccRepo=github.com/dvidelabs/flatcc + +scriptDir=$(dirname "${BASH_SOURCE[0]}") +srcDir=${scriptDir}/src +buildDir=${scriptDir}/build +installDir=${scriptDir} + +buildType=Release +configArgs="-DCMAKE_BUILD_TYPE=${buildType}" + +if [[ "$(uname)" == MINGW* ]] || [[ "$(uname)" == CYGWIN* ]]; then + configArgs+=' -G "MinGW Makefiles"' + # aligned_alloc() would be missing on MinGW, see https://github.com/dvidelabs/flatcc/issues/155 + # It's fixed now on master, the following line can be removed after upgrading to a new release + export CFLAGS="-DFLATCC_USE_GENERIC_ALIGNED_ALLOC=1" +fi + +function prepare() { + echo "******** Getting Flatcc sources ********" + if [[ ! -d ${srcDir} ]]; then + echo "Cloning ${fccRepo} into Into: ${srcDir}" + git clone https://${fccRepo}.git "${srcDir}" + fi + + echo "Checking out ${fccVersion}" + (cd "${srcDir}"; git fetch) + (cd "${srcDir}"; git checkout ${fccVersion}) +} + +function build() { + echo "******** Configuring & building ********" + + # Note: we need an absolute path... + # realpath isn't available on macOS and the "else" variant didn't work well on windows because the path was already absolute... + srcDirAbsolute= + if [[ -x $(command -v realpath) ]]; then + srcDirAbsolute=$(realpath "$srcDir") + else + srcDirAbsolute="$(pwd)/$srcDir" + fi + + pwd=$(pwd) + mkdir -p "$buildDir" + + set -x + + # need to use eval because of quotes in configArgs... bash is just wonderful... + cd "$buildDir" + eval "cmake \"$srcDirAbsolute\" $configArgs" + cd "$pwd" + + cmake --build "$buildDir" --config ${buildType} --target flatccrt + set +x +} + +function install() { + echo "******** Collecting artifacts ********" + echo "Copying from ${srcDir} to ${installDir}:" + cp -rv "${srcDir}/include" "${installDir}" + if [[ -d "${srcDir}/lib/${buildType}" ]]; then + cp -rv "${srcDir}/lib/${buildType}" "${installDir}/lib" + else + cp -rv "${srcDir}/lib" "${installDir}" + fi +} + +prepare +build +install diff --git a/third_party/flatcc/clean.sh b/third_party/flatcc/clean.sh new file mode 100755 index 00000000..e97f6047 --- /dev/null +++ b/third_party/flatcc/clean.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +scriptDir=$(dirname "${BASH_SOURCE[0]}") +set +x +rm -rf "${scriptDir}/build" +rm -rf "${scriptDir}/include" +rm -rf "${scriptDir}/lib" +rm -rf "${scriptDir}/src" \ No newline at end of file diff --git a/third_party/objectbox-c/.gitignore b/third_party/objectbox-c/.gitignore new file mode 100644 index 00000000..5c15c317 --- /dev/null +++ b/third_party/objectbox-c/.gitignore @@ -0,0 +1,3 @@ +download +include +lib \ No newline at end of file diff --git a/third_party/objectbox-c/clean.sh b/third_party/objectbox-c/clean.sh new file mode 100755 index 00000000..66aa5de5 --- /dev/null +++ b/third_party/objectbox-c/clean.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +scriptDir=$(dirname "${BASH_SOURCE[0]}") +set +x +rm -rf "${scriptDir}/download" +rm -rf "${scriptDir}/include" +rm -rf "${scriptDir}/lib" \ No newline at end of file diff --git a/third_party/objectbox-c/get-objectbox-c.sh b/third_party/objectbox-c/get-objectbox-c.sh new file mode 100755 index 00000000..b11922c4 --- /dev/null +++ b/third_party/objectbox-c/get-objectbox-c.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +cVersion=0.9.0 + +scriptDir=$(dirname "${BASH_SOURCE[0]}") + + +echo "******** Downloading ObjectBox-C library ********" +echo "Into: ${scriptDir}" +cd "${scriptDir}" + +# don't install the library system-wide, just download it +export installLibrary=false +bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-c/master/download.sh) --quiet ${cVersion} + +echo "******** Collecting artifacts ********" +cp -rfv download/testing/*objectbox*/include ./ +cp -rfv download/testing/*objectbox*/lib ./ +rm -rfv download + +echo "Downloaded ObjectBox-C headers and library:" +echo "Current directory: ${scriptDir}" +ls -lh include lib \ No newline at end of file