zig creating a C library - zig

Closed - see the end of the entry
I want to make a C callable library using the zig language.
I decided to start with the two examples in the Zig documentation. "Exporting a C Library" and "Mixing Object Files".
In each case I copied the three relevant files(from 0.6.0 docs).
A C file called test.c,
a zig file called respectively mathtest.zig (for the Exporting a C Library example) and base64.zig (for the Mixing Object Files example), and
a build.zig file.
Both examples fail to build.
The Exporting a C Library example fails to compile test.c with a message cannot find mathtest.h
The Mixing Object Files example fails to compile test.c with cannot find base64.h
Here are the three files for the Exporting C Libary example:
mathtest.zig
export fn add(a: i32, b: i32) i32 {
return a + b;
}
test.c
// This header is generated by zig from mathtest.zig
#include "mathtest.h"
#include <stdio.h>
int main(int argc, char **argv) {
int32_t result = add(42, 1337);
printf("%d\n", result);
return 0;
}
build.zig
const Builder = #import("std").build.Builder;
pub fn build(b: *Builder) void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addExecutable("test", null);
exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
exe.linkLibrary(lib);
exe.linkSystemLibrary("c");
b.default_step.dependOn(&exe.step);
const run_cmd = exe.run();
const test_step = b.step("test", "Test the program");
test_step.dependOn(&run_cmd.step);
}
Part of the error message
~/Projects/zig/z-c-lib $ zig build test
/home/robert/Projects/zig/z-c-lib/test.c:2:10: fatal error: 'mathtest.h' file not found
#include "mathtest.h"
^~~~~~~~~~~~
1 error generated.
The following command failed:
/home/robert/zig/zig clang -c -nostdinc -fno-spell-checking -target x86_64-unknown-linux-gnu -isystem /home/robert/zig/lib/zig/include -isystem /home/robert/zig/lib/zig/libc/include/x86_64-linux-gnu -isystem /home/robert/zig/lib/zig/libc/include/generic-glibc -isystem /home/robert/zig/lib/zig/libc/include/x86_64-linux-any -isystem /home/robert/zig/lib/zig/libc/include/any-linux-any -Xclang -target-cpu -Xclang znver2 -Xclang -target-feature -Xclang -3dnow -Xclang -target-feature -Xclang -3dnowa -Xclang -target-feature -Xclang +64bit -Xclang -target-feature -Xclang +adx -Xclang -target-feature -Xclang +aes -Xclang -target-feature -Xclang +avx -Xclang -target-feature -Xclang +avx2 -Xclang -target-feature -Xclang -avx512bf16 -Xclang -target-feature -Xclang -avx512b
I cannot find a file named mathtest.h on my system so I assume it was not generated, contrary to the claim in the test.c file.
What am I missing ? Help gratefully accepted.
Answers and More questions
I discovered the -femit-h options:
zig build-lib mathtest.zig -femit-h
will create a mathtest.h file and then
zig build
will be successful.
I further discovered that
these lines in the build.zig files
const lib = b.addSharedLibrary('mathtest', 'mathtest.zig', b.version(1, 0, 0));
lib.femit_h = true;
will ensure that
zig build test
will be successful and generate the answer 1379 as indicated in the docs.
BUT - this mod to the build.zig file does not leave a mathtest.h file around after it runs.
That seems to be the final hurdle to generating a usable C library from a Zig code.
Final piece in the puzzle
If I add
lib.setOutputDir("build");
to the build.zig file. The mathtest.h and libmathtest.a (or .so) files will be saved into a build dir.
Call this one close

Ok so part of the answer is simple, but maybe obscure, the -femit-h options.
The command
zig build-lib mathtest.zig -femti-h
will generate a mathtest.h file.
But how do I add that options to the
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
line in the build.zig file.

Related

why does clang can't using in this way

//a.c
int main() {
printf("Hello world\n");
}
//currently in the llvm-project directory
clang -Xclang -load -Xclang build/lib/LLVMHello.so a.c
the command above doesn't take any effect. while the following commond take effect
clang -emit-llvm -c a.c
opt -load build/lib/LLVMHello.so -hello < a.bc
This is because the LLVMHello demo register the pass by
static RegisterPass<Hello> X("hello", "Hello World Pass");
As it is only effective for OPT.
If you want to load it to clang, you can replace the RegisterPass with
static void registerHelloPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) {
PM.add(new Hello());
}
static RegisterStandardPasses RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, registerHelloPass);
Then you could use clang -Xclang -load -Xclang XXX.so. In this way, you would load it either in clang or in opt.

order of imports for clang libraries

I'm writing my own Makefile to compile my minimum standalone clang tool consisting of the following code:
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
using namespace clang::driver;
using namespace clang::tooling;
static llvm::cl::OptionCategory ToolingSampleCategory("MetaLift Clang Frontend");
int main (int argc, const char **argv)
{
CommonOptionsParser op(argc, argv, ToolingSampleCategory);
ClangTool Tool(op.getCompilations(), op.getSourcePathList());
return 0;
}
Is there a "proper" order to import the clang libraries? My current order is:
-lclangTooling -lclangSerialization -lclangFrontend
But I keep running into undefined symbols issues.
I find using CMake to be the easiest way to build a libTooling-based application. Here is a minimalistic snippet to compile your example:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
# Find CMake file for Clang
find_package(Clang REQUIRED)
# Add path to LLVM modules
set(CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH}
"${LLVM_CMAKE_DIR}"
)
# import LLVM CMake functions
include(AddLLVM)
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CLANG_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
add_definitions(${CLANG_DEFINITIONS})
add_llvm_executable(myTool main.cpp)
set_property(TARGET myTool PROPERTY CXX_STANDARD 11)
target_link_libraries(myTool PRIVATE clangTooling)
// main.cpp
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
using namespace clang::driver;
using namespace clang::tooling;
static llvm::cl::OptionCategory
ToolingSampleCategory("MetaLift Clang Frontend");
int main(int argc, const char **argv) {
CommonOptionsParser op(argc, argv, ToolingSampleCategory);
ClangTool Tool(op.getCompilations(), op.getSourcePathList());
return 0;
}
I checked the actual commands happening during the build:
c++ -DGTEST_HAS_RTTI=0 -I${CLANG}/include -O3 -DNDEBUG -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fno-exceptions -fno-rtti -std=gnu++11 -o CMakeFiles/myTool.dir/main.cpp.o -c /Users/vsavchenko/source/c++/clang-makefile/main.cpp
c++ -O3 -DNDEBUG -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names -Wl,-dead_strip CMakeFiles/myTool.dir/main.cpp.o -o myTool -Wl,-rpath,#loader_path/../lib -Wl,-rpath,${CLANG}/lib ${CLANG}/lib/libclangTooling.a ${CLANG}/lib/libclangASTMatchers.a ${CLANG}/lib/libclangFormat.a ${CLANG}/lib/libclangFrontend.a ${CLANG}/lib/libclangDriver.a ${CLANG}/lib/libclangParse.a ${CLANG}/lib/libLLVMMCParser.a ${CLANG}/lib/libclangSerialization.a ${CLANG}/lib/libclangSema.a ${CLANG}/lib/libclangEdit.a ${CLANG}/lib/libclangAnalysis.a ${CLANG}/lib/libLLVMBitReader.a ${CLANG}/lib/libLLVMProfileData.a ${CLANG}/lib/libclangToolingCore.a ${CLANG}/lib/libclangAST.a ${CLANG}/lib/libclangRewrite.a ${CLANG}/lib/libclangLex.a ${CLANG}/lib/libclangBasic.a ${CLANG}/lib/libLLVMCore.a ${CLANG}/lib/libLLVMBinaryFormat.a ${CLANG}/lib/libLLVMMC.a ${CLANG}/lib/libLLVMOption.a ${CLANG}/lib/libLLVMSupport.a -lz -lcurses -lm /Users/vsavchenko/bin/clang/lib/libLLVMDemangle.a
It looks like the problem is not with the order of Clang libraries, but rather an insufficient set of them. The following command finishes without linking errors:
LIBRARY_PATH=${CLANG}/lib clang++ -std=c++11 -lclangTooling -lclangASTMatchers -lclangFormat -lclangFrontend -lclangDriver -lclangParse -lLLVMMCParser -lclangSerialization -lclangSema -lclangEdit -lclangAnalysis -lLLVMBitReader -lLLVMProfileData -lclangToolingCore -lclangAST -lclangRewrite -lclangLex -lclangBasic -lLLVMCore -lLLVMBinaryFormat -lLLVMMC -lLLVMOption -lLLVMSupport -lz -lcurses -lLLVMDemangle -I${CLANG}/include main.cpp
Happy hacking with Clang!

How to view Clang AST?

I am trying to get hold on Clang. So, I would like to view the AST generated by Clang after parsing the given program. Is it possible to dump AST in .dot or .viz format? Is there any tool out there?
The method with -cc1 invocation will have problem with includes and recognizing C++.
For full-featured parsing, use:
clang -Xclang -ast-dump file.cpp
Clang supports showing the AST with Graphviz's dotty -- you can grab the temporary .dot file generated (name is printed out) to get the graph source.
clang -cc1 -ast-view your_file.c
You can also print to the command line with:
clang -cc1 -ast-dump your_file.c
or:
clang -cc1 -ast-print your_file.c
or in 3.3:
clang -cc1 -ast-dump-xml your_file.c
but this was removed later as pointed by Lukas Kubanek in the comment.
For viewing the AST
clang-check -ast-dump filename.c
For to view the specific functions in a program
clang-check -ast-dump -ast-dump-filter=function_name filename.c
I am using following:
clang my_file.h -I. -Xclang -ast-dump -fsyntax-only -fno-color-diagnostics -Wno-visibility
IMHO This is more suitable for machine parsing.

How to use precompiled headers with a qmake extra compiler?

When using the default qmake compiler (via the SOURCES variable), I can use precompiled headers like so:
CONFIG += precompile_header
PRECOMPILED_HEADER = stable.h
SOURCES = main.c
However, I'd like to use a custom compiler (via QMAKE_EXTRA_COMPILERS). I tried this:
CONFIG += precompile_header
PRECOMPILED_HEADER = stable.h
MY_SOURCES = main.c
my.input = MY_SOURCES
my.output = ${QMAKE_FILE_IN_BASE}.o
my.commands = clang $$QMAKE_CFLAGS_USE_PRECOMPILE -c ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}
QMAKE_EXTRA_COMPILERS += my
...and the precompiled headers are built, but my custom compiler fails because QMAKE_CFLAGS_USE_PRECOMPILE doesn't contain the path to the precompiled header. (It is defined as -Xclang -include-pch -Xclang ${QMAKE_PCH_OUTPUT}, and apparently ${QMAKE_PCH_OUTPUT} is empty.)
How can I get the name of the generated precompiled header, so I can pass it as a parameter to my custom compiler?
Looking at the qmake source code, precompiled header handling seems to be hardcoded in UnixMakefileGenerator::init() to only work for the built-in C, CXX, OBJC, and OBJCXX compilers.
QMAKE_PCH_OUTPUT, for GCC and Clang-style precompiled headers, is constructed by combining PRECOMPILED_DIR, TARGET, one of c/c++/objective-c/objective-c++, and QMAKE_PCH_OUTPUT_EXT. So, in the question's second example, the following command line should work:
my.commands = clang -Xclang -include-pch -Xclang $$TARGET/c$$QMAKE_PCH_OUTPUT_EXT -c ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT}

Is it possible code coverage of a shared library using gcov?

I try to test an executable which uses OpenCV shared library. When using gcov to know what code lines were covered I only get info about my .cpp files and .hpp of the library. No info is shown about .cpp files of the library.
I compiled and linked with -pg --coverage flags.
Yes, gcov can give coverage information about a shared library. If I remember correctly from the problems I had getting this to work on my project, you're probably not including the --coverage flag on the linking of the dynamic library. Here's the smallest example I could create.
Makefile:
CXXFLAGS += --coverage
LDFLAGS += --coverage
myexec: myexec.cpp libmylib.so
libmylib.so: mylib.o
gcc --coverage -shared -Wl,-soname,libmylib.so -o libmylib.so mylib.o
mylib.o: CXXFLAGS += -fPIC
myexec.cpp:
#include "mylib.h"
int main(int argc, char** argv)
{
return is_even(argc);
}
mylib.h
#ifndef MYLIB_H
#define MYLIB_H
int is_even(int num);
#endif
mylib.cpp
#include "mylib.h"
int is_even(int num)
{
if (num % 2)
return false;
else
return true;
}
Output of make (so you can see exactly what the build was):
g++ --coverage -fPIC -c -o mylib.o mylib.cpp
gcc --coverage -shared -Wl,-soname,libmylib.so -o libmylib.so mylib.o
g++ --coverage --coverage myexec.cpp libmylib.so -o myexec
I ran the executable using LD_LIBRARY_PATH="." ./myexec a, and then ran gcov mylib.cpp. Here's the contents of mylib.cpp.gcov:
-: 0:Source:mylib.cpp
-: 0:Graph:mylib.gcno
-: 0:Data:mylib.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include "mylib.h"
-: 2:
1: 3:int is_even(int num)
-: 4:{
1: 5: if (num % 2)
#####: 6: return false;
-: 7: else
1: 8: return true;
-: 9:}

Resources