Compilation, Libraries, Debug, Automation
Polytech Lille · Embedded Systems
Jeremie Dequidt
Program | Software / App |
---|---|
1/2 devs | > 10 devs |
1/2 file | > 1k files |
500 LoCs | 1M LoCs |
1 arch. | n arch. |
1 version | n versions + m patches |
no deps | libs + deps |
easy to debug | really not easy |
10s to build | 4h to build |
Expectation | Reality |
Source file (.c) | Source file (.c) |
Pre-processor | |
Abstract Syntax Tree | |
Intermediate Code | |
Assembler Code | |
Binary Code | |
Link Editor | |
Executable | Executable |
gcc -E my_prog.c
util.h
int compare(int a, int b)
{
if (a == b) return 0;
if (a < b) return 1;
return -1;
}
my_prog.c
#include "util.h"
#define N 20
#define M 35
int main()
{
int ret_value = compare(N, M);
return ret_value;
}
my_prog.e
# 1 "" 2
# 1 "my_prog.c" 2
# 1 "./util.h" 1
int compare(int a, int b)
{
if (a == b) return 0;
if (a < b) return 1;
return -1;
}
# 2 "my_prog.c" 2
int main()
{
int ret_value = compare(20, 35);
return ret_value;
}
gcc -Xclang -ast-dump -fsyntax-only my_prog.c
[...]
|-FunctionDecl 0x7fd2be01a970 <./util.h:1:1, line:6:1> line:1:5 used compare 'int (int, int)'
| |-ParmVarDecl 0x7fd2be01a818 col:17 used a 'int'
| |-ParmVarDecl 0x7fd2be01a898 col:24 used b 'int'
| `-CompoundStmt 0x7fd2be01ac60
| |-IfStmt 0x7fd2be01ab28
| | |-BinaryOperator 0x7fd2be01aad8 'int' '=='
| | | |-ImplicitCastExpr 0x7fd2be01aaa8 'int'
| | | | `-DeclRefExpr 0x7fd2be01aa68 'int' lvalue ParmVar 0x7fd2be01a818 'a' 'int'
| | | `-ImplicitCastExpr 0x7fd2be01aac0 'int'
| | | `-DeclRefExpr 0x7fd2be01aa88 'int' lvalue ParmVar 0x7fd2be01a898 'b' 'int'
| | `-ReturnStmt 0x7fd2be01ab18
| | `-IntegerLiteral 0x7fd2be01aaf8 'int' 0
[...]
gcc -S -emit-llvm my_prog.c
; ModuleID = 'my_prog.c'
source_filename = "my_prog.c"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx11.0.0"
; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @compare(i32 %0, i32 %1) #0 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i32, align 4
store i32 %0, i32* %4, align 4
store i32 %1, i32* %5, align 4
%6 = load i32, i32* %4, align 4
%7 = load i32, i32* %5, align 4
%8 = icmp eq i32 %6, %7
br i1 %8, label %9, label %10
9: ; preds = %2
store i32 0, i32* %3, align 4
br label %16
10: ; preds = %2
%11 = load i32, i32* %4, align 4
%12 = load i32, i32* %5, align 4
%13 = icmp slt i32 %11, %12
br i1 %13, label %14, label %15
14: ; preds = %10
store i32 1, i32* %3, align 4
br label %16
15: ; preds = %10
store i32 -1, i32* %3, align 4
br label %16
16: ; preds = %15, %14, %9
%17 = load i32, i32* %3, align 4
ret i32 %17
}
; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
store i32 0, i32* %1, align 4
%3 = call i32 @compare(i32 20, i32 35)
store i32 %3, i32* %2, align 4
%4 = load i32, i32* %2, align 4
ret i32 %4
}
attributes #0 = { noinline nounwind optnone ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "darwin-stkchk-strong-link" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 11, i32 1]}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 7, !"PIC Level", i32 2}
!3 = !{!"Apple clang version 12.0.0 (clang-1200.0.32.29)"}
gcc -S my_prog.c
my_prog.S
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0 sdk_version 11, 1
.globl _compare ## -- Begin function compare
.p2align 4, 0x90
_compare: ## @compare
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl %edi, -8(%rbp)
movl %esi, -12(%rbp)
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
jne LBB0_2
## %bb.1:
movl $0, -4(%rbp)
jmp LBB0_5
LBB0_2:
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
jge LBB0_4
## %bb.3:
movl $1, -4(%rbp)
jmp LBB0_5
LBB0_4:
movl $-1, -4(%rbp)
LBB0_5:
movl -4(%rbp), %eax
popq %rbp
retq
.cfi_endproc
## -- End function
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $0, -4(%rbp)
movl $20, %edi
movl $35, %esi
callq _compare
movl %eax, -8(%rbp)
movl -8(%rbp), %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
## -- End function
.subsections_via_symbols
as my_prog.s
my_prog.o
0000000 cf fa ed fe 07 00 00 01 03 00 00 00 01 00 00 00
0000010 04 00 00 00 b8 01 00 00 00 20 00 00 00 00 00 00
0000020 19 00 00 00 38 01 00 00 00 00 00 00 00 00 00 00
0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000040 28 01 00 00 00 00 00 00 d8 01 00 00 00 00 00 00
0000050 28 01 00 00 00 00 00 00 07 00 00 00 07 00 00 00
0000060 03 00 00 00 00 00 00 00 5f 5f 74 65 78 74 00 00
0000070 00 00 00 00 00 00 00 00 5f 5f 54 45 58 54 00 00
0000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000090 7a 00 00 00 00 00 00 00 d8 01 00 00 04 00 00 00
00000a0 00 03 00 00 01 00 00 00 00 04 00 80 00 00 00 00
00000b0 00 00 00 00 00 00 00 00 5f 5f 63 6f 6d 70 61 63
00000c0 74 5f 75 6e 77 69 6e 64 5f 5f 4c 44 00 00 00 00
00000d0 00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00000e0 40 00 00 00 00 00 00 00 58 02 00 00 03 00 00 00
00000f0 08 03 00 00 02 00 00 00 00 00 00 02 00 00 00 00
0000100 00 00 00 00 00 00 00 00 5f 5f 65 68 5f 66 72 61
0000110 6d 65 00 00 00 00 00 00 5f 5f 54 45 58 54 00 00
0000120 00 00 00 00 00 00 00 00 c0 00 00 00 00 00 00 00
0000130 68 00 00 00 00 00 00 00 98 02 00 00 03 00 00 00
0000140 00 00 00 00 00 00 00 00 0b 00 00 68 00 00 00 00
0000150 00 00 00 00 00 00 00 00 32 00 00 00 18 00 00 00
0000160 01 00 00 00 00 00 0b 00 00 01 0b 00 00 00 00 00
0000170 02 00 00 00 18 00 00 00 18 03 00 00 02 00 00 00
0000180 38 03 00 00 10 00 00 00 0b 00 00 00 50 00 00 00
0000190 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00
00001a0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
00001d0 00 00 00 00 00 00 00 00 55 48 89 e5 89 7d f8 89
00001e0 75 f4 8b 45 f8 3b 45 f4 0f 85 0c 00 00 00 c7 45
00001f0 fc 00 00 00 00 e9 1f 00 00 00 8b 45 f8 3b 45 f4
0000200 0f 8d 0c 00 00 00 c7 45 fc 01 00 00 00 e9 07 00
0000210 00 00 c7 45 fc ff ff ff ff 8b 45 fc 5d c3 66 2e
0000220 0f 1f 84 00 00 00 00 00 55 48 89 e5 48 83 ec 10
0000230 c7 45 fc 00 00 00 00 bf 14 00 00 00 be 23 00 00
0000240 00 e8 00 00 00 00 89 45 f8 8b 45 f8 48 83 c4 10
0000250 5d c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0000260 46 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
0000270 00 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00
0000280 2a 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
0000290 00 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
00002a0 01 7a 52 00 01 78 10 01 10 0c 07 08 90 01 00 00
00002b0 24 00 00 00 1c 00 00 00 20 ff ff ff ff ff ff ff
00002c0 46 00 00 00 00 00 00 00 00 41 0e 10 86 02 43 0d
00002d0 06 00 00 00 00 00 00 00 24 00 00 00 44 00 00 00
00002e0 48 ff ff ff ff ff ff ff 2a 00 00 00 00 00 00 00
00002f0 00 41 0e 10 86 02 43 0d 06 00 00 00 00 00 00 00
0000300 6a 00 00 00 00 00 00 2d 20 00 00 00 01 00 00 06
0000310 00 00 00 00 01 00 00 06 07 00 00 00 0f 01 00 00
0000320 00 00 00 00 00 00 00 00 01 00 00 00 0f 01 00 00
0000330 50 00 00 00 00 00 00 00 00 5f 6d 61 69 6e 00 5f
0000340 63 6f 6d 70 61 72 65 00
str_ex.c
int main()
{
const char line[] = "Hello SE3 !";
return 0;
}
str_ex.o
[...]
0000270 31 c0 48 83 c4 20 5d c3 e8 00 00 00 00 0f 0b 48
0000280 65 6c 6c 6f 20 53 45 33 20 21 00 00 00 00 00 00
[...]
echo "Hello SE3 \!" | hexdump
0000000 48 65 6c 6c 6f 20 53 45 33 20 21 0a
ld_util.c
int compare(int a, int b)
{
if (a == b) return 0;
if (a < b) return 1;
return -1;
}
ld_main.c
#define N 20
#define M 35
int compare(int, int);
int main()
{
int ret_value = compare(N, M);
return ret_value;
}
$ gcc ld_main.c
Undefined symbols for architecture x86_64:
"_compare", referenced from:
_main in ld_main-f1b913.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1
(use -v to see invocation)
$ gcc -c ld_main.c
$ gcc -c ld_util.c
$ nm ld_main.o
U _compare
0000000000000000 T _main
$ nm ld_util.o
0000000000000000 T _compare
$ gcc ld_main.o ld_util.o
$ nm a.out
0000000100000000 T __mh_execute_header
0000000100003f70 T _compare
0000000100003f40 T _main
U dyld_stub_binder
if a task is repetitive, automate it !
Build easy to read / easy to maintain code
This allows to control dependencies between module, allows to build libraries...
$ cloc .
600 text files.
591 unique files.
53 files ignored.
-------------------------------------------
Language files blank comment code
-------------------------------------------
C 328 37249 58865 192435
C/C++ Header 143 6285 7688 23982
C++ 8 725 724 2438
make 3 214 230 1792
Assembly 9 267 406 1224
Bourne Shell 8 61 48 305
[...]
-------------------------------------------
SUM: 547 47047 71726 248147
-------------------------------------------
$ tree -d
.
├── botlib
├── bspc
├── cgame
├── client
├── game
├── jpeg-6
├── macosx
├── null
├── q3_ui
├── qcommon
├── renderer
├── server
├── splines
├── ui
├── unix
└── win32
inout.h
#define TAILLE_MAX 256
#define INTENSITE_MAX 255
#define LARGEUR 800
#define HAUTEUR 600
int chargeImage(const char *nom_fichier,
unsigned char tableau[HAUTEUR][LARGEUR]);
int sauvegardeImage(const char *nom_fichier,
unsigned char tableau[HAUTEUR][LARGEUR]);
inout.c
/*
* Projet de Programmation Structurée
* Département IMA - 3ieme Année
* Polytech'Lille - 2010/2011
* By Jérémie Dequidt
*/
#include "inout.h"
#define DEBUG true
/*********************************************************
* Base functions : input/output of pictures
*********************************************************
*/
/*
* Load a PGM picture into a char vect
* - Parameters :
* nom_fichier: The input file
* vecteur: char pointer where the pixels are stored
* - Return value : 1 if everything went well, 0 otherwise
*/
int chargeImage(const char *nom_fichier,
unsigned char vecteur[HAUTEUR][LARGEUR])
{
/* Variable Declaration */
FILE *fp;
unsigned int type;
int imax, l, h, debug;
unsigned char buffer[TAILLE_MAX];
// [...]
}
/*
\brief Fonction to save a picture
\param nom_fichier output file where we want to save
\param type output type (grey / color)
\param tableau char array (= where to find pixels)
\return 1 if everything went well, 0 otherwise
*/
int sauvegardeImage(const char *nom_fichier,
unsigned char vecteur[HAUTEUR][LARGEUR])
{
FILE *fp;
/* File opening */
[...]
}
main.c
#include
#include
#include "inout.h"
//------------FONCTIONS--------------------
//image en négatif
void negatif(unsigned char imagea[HAUTEUR][LARGEUR]) {
for(int i=0; i< HAUTEUR; i++) {
for (int j = 0; j < LARGEUR; j++) {
imagea[i][j] = 255 - imagea[i][j]; // assign inverse of pixel intensity
}
}
}
//[...]
int main()
{
unsigned char image[HAUTEUR][LARGEUR], image2[HAUTEUR][LARGEUR];
int charge = chargeImage("../images/royalPalaceMadrid.pgm", image);
int charge2 = chargeImage("../images/fog-1535201_800.pgm", image2);
// [...]
return 0;
}
Remember pre-processing step in compilation
// a.h
const int g_variable = 50;
// b.h
#include "a.h"
int func()
{
return g_variable;
}
// main.c
#include "a.h"
#include "b.h"
int main()
{
int var = g_variable;
return var + func();
}
Remember pre-processing step in compilation
$ gcc main.c
In file included from main.c:2:
In file included from ./b.h:1:
./a.h:1:11: error: redefinition of 'g_variable'
const int g_variable = 50;
^
main.c:1:10: note: './a.h' included multiple times,
additional include site here
#include "a.h"
^
main.e
# 1 "main.c"
# 1 "./a.h" 1
const int g_variable = 50;
# 2 "main.c" 2
# 1 "./b.h" 1
# 1 "./a.h" 1
const int g_variable = 50;
# 2 "./b.h" 2
int func()
{
return g_variable;
}
# 3 "main.c" 2
int main()
{
int var = g_variable;
return var + func();
}
// c.h
#include "d.h"
int g_var = 10;
int temp_c()
{
return temp_d() + g_var;
}
// d.h
#include "c.h"
int temp_d()
{
return temp_c() + g_var;
}
// main.c
#include "c.h"
int main()
{
return temp_c();
}
# 1 "maine.c"
# 1 "" 1
# 1 "" 3
# 366 "" 3
# 1 "" 1
# 1 "" 2
# 1 "maine.c" 2
# 1 "./c.h" 1
# 1 "./d.h" 1
# [... repeated 200x]
int g_var = 10;
int temp_c()
{
return temp_d() + g_var;
}
# 2 "./d.h" 2
int temp_d()
{
return temp_c() + g_var;
}
# 2 "./c.h" 2
int g_var = 10;
int temp_c()
{
return temp_d() + g_var;
}
# 2 "./d.h" 2
// [...]
# 2 "maine.c" 2
int main()
{
return temp_c();
}
#ifndef __HEADER_NAME__
#define __HEADER_NAME__
// file content
#endif
gcc -c
...gcc -o
commands are requiredMakefile
Makefile
contains comments ...
# Makefile for project S5
TARGET= project
CFLAGS=-g -W -Wall -Wextra
LDFLAGS=-lm
default: $(TARGET)
inout.o: inout.c inout.h
gcc $(CFLAGS) -c inout.c
main.o: main.c inout.h
gcc $(CFLAGS) -c main.c
$(TARGET): main.o inout.o
gcc $(LDFLAGS) main.o inout.o -o $(TARGET)
.PHONY: clean
clean:
rm -f *.o
rm -f $(TARGET)
target: list of dependencies
[TAB]command(s) to build the target
make
is equivalent to
make -f Makefile default
assumes that the default target exists
main.o: main.c inout.h
gcc $(CFLAGS) -c main.c
$(TARGET): main.o inout.o
gcc $(LDFLAGS) main.o inout.o -o $(TARGET)
$@ | the target |
$* | the target without extension |
$< | the first dependency |
$^ | all the dependencies |
inout.o: inout.c inout.h
gcc $(CFLAGS) -c $<
main.o: main.c inout.h
gcc $(CFLAGS) -c $<
$(TARGET): main.o inout.o
gcc $(LDFLAGS) $^ -o $@
inout.o: inout.c inout.h
gcc $(CFLAGS) -c $<
main.o: main.c inout.h
gcc $(CFLAGS) -c $<
.c.o:
gcc $(CFLAGS) -c $<
defines how c files are built in object files
TARGET= project
CFLAGS=-g -W -Wall -Wextra
LDFLAGS=-lm
default: $(TARGET)
.c.o:
gcc $(CFLAGS) -c $<
$(TARGET): main.o inout.o
gcc $(LDFLAGS) $^ -o $@
.PHONY: clean
clean:
rm -f *.o
rm -f $(TARGET)
.c.o:
gcc $(CFLAGS) -c $<
dependency of headers is lost 🙁
$ gcc -MMD -c ld_main.c
$ cat ld_main.d
ld_main.o: ld_main.c
for each source file (.c), a corresponding .d file is generated that contains dependencies
TARGET= project
CFLAGS=-g -W -Wall -Wextra -MMD
LDFLAGS=-lm
DEPS=main.d inout.d
default: $(TARGET)
.c.o:
gcc $(CFLAGS) -c $<
$(TARGET): main.o inout.o
gcc $(LDFLAGS) $^ -o $@
-include $(DEPS)
.PHONY: clean
clean:
rm -f *.o
rm -f $(TARGET)
Automatic list of files
SRC=$(wildcard *.c)
every source file (.c) in the current folder
DEPS=$(SRC:.c=.d)
generates a list of filenames where .c extension is replaced by .d extension
TARGET= project
CFLAGS=-g -W -Wall -Wextra -MMD
LDFLAGS=-lm
SRC=$(wildcard *.c)
DEPS=$(SRC:.c=.d)
OBJ=$(SRC:.c=.o)
default: $(TARGET)
.c.o:
gcc $(CFLAGS) -c $<
$(TARGET): $(OBJ)
gcc $(LDFLAGS) $^ -o $@
-include $(DEPS)
.PHONY: clean
clean:
rm -f *.o
rm -f $(TARGET)
DEBUG=yes #no
ifeq ($(DEBUG),yes)
CFLAGS=-W -Wall -ansi -g -DDEBUG
else
CFLAGS=-O3
endif
these libraries are now linked automatically in any C program
libNAME.yy
-lNAME
for gcc
/usr/lib/
and /usr/local/lib/
, if your libraries are located elsewhere you need to specifiy the location with -Lpath
Libraries can be static or dynamic
Embedded in the program
linking done at execution
How to be sure that a program with dynamic libs can run on other computers ?
A dynamic lib follows the pattern: libNAME.M.m.p.EXT
$ ls -l /usr/local/lib/libgpg*
lrwxr-xr-x 52 jeremie 17 Nov 2020 /usr/local/lib/libgpg-error.0.dylib -> ../Cellar/libgpg-error/1.39/lib/libgpg-error.0.dylib
lrwxr-xr-x 46 jeremie 17 Nov 2020 /usr/local/lib/libgpg-error.a -> ../Cellar/libgpg-error/1.39/lib/libgpg-error.a
lrwxr-xr-x 50 jeremie 17 Nov 2020 /usr/local/lib/libgpg-error.dylib -> ../Cellar/libgpg-error/1.39/lib/libgpg-error.dylib
lrwxr-xr-x 44 jeremie 17 Nov 2020 /usr/local/lib/libgpgme.11.dylib -> ../Cellar/gpgme/1.15.0/lib/libgpgme.11.dylib
lrwxr-xr-x 37 jeremie 17 Nov 2020 /usr/local/lib/libgpgme.a -> ../Cellar/gpgme/1.15.0/lib/libgpgme.a
lrwxr-xr-x 41 jeremie 17 Nov 2020 /usr/local/lib/libgpgme.dylib -> ../Cellar/gpgme/1.15.0/lib/libgpgme.dylib
lrwxr-xr-x 45 jeremie 17 Nov 2020 /usr/local/lib/libgpgmepp.6.dylib -> ../Cellar/gpgme/1.15.0/lib/libgpgmepp.6.dylib
lrwxr-xr-x 39 jeremie 17 Nov 2020 /usr/local/lib/libgpgmepp.a -> ../Cellar/gpgme/1.15.0/lib/libgpgmepp.a
lrwxr-xr-x 43 jeremie 17 Nov 2020 /usr/local/lib/libgpgmepp.dylib -> ../Cellar/gpgme/1.15.0/lib/libgpgmepp.dylib
When you link a library...
gcc test.c -lgpgme
by default the one with the biggest M,m,p is chosen
$ ldd /usr/bin/gcc
linux-vdso.so.1 (0x00007fff967eb000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fce65c6d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fce65aac000)
/lib64/ld-linux-x86-64.so.2 (0x00007fce65e00000)
#include
#include
int main(int argc, char** argv)
{
char * buffer = malloc(1024);
sprintf(buffer, "%d", argc);
printf("%s",buffer);
}
$ gcc test.c
$ ldd a.out
linux-vdso.so.1 (0x00007ffd099a3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7926644000)
/lib64/ld-linux-x86-64.so.2 (0x00007f792681a000)
to create libZZZZ.a
$ ar rcs libZZZZ.a xxxx.o yyyy.o
$ ar -t libXXXX.a
xxxx.o
yyyy.o
to create libtest.so
$ gcc [options] -fPIC -c xxxx.c yyyy.c
$ gcc -shared -Wl,-soname,libtest.so.1 -o libtest.so.1.0 *.o
clang-format for automatic formatting
clang-format -I fichier.c
clang-format
for automatic formattingclang-format
can be included in most code editors (vim, Sublime Text, Visual Studio...)
#include
#include
#include "inout.h"
// selection par Rectangle
void selectionR(unsigned char m[HAUTEUR][LARGEUR],int x,int y,int x1,int y1, unsigned char reception[HAUTEUR][LARGEUR]){
int j,i;
for(i=x;i< x1;i++)
for(j=y;j< y1;++j){
reception[i][j]=m[i][j];
m[i][j]=0;
}
}
#include
#include
#include "inout.h"
// selection par Rectangle
void selectionR (unsigned char m[HAUTEUR][LARGEUR],
int x,
int y,
int x1,
int y1,
unsigned char reception[HAUTEUR][LARGEUR])
{
int j, i;
for (i = x; i < x1; i++)
for (j = y; j < y1; ++j)
{
reception[i][j] = m[i][j];
m[i][j] = 0;
}
}
Applying DRY with pmd
(duplicates search)
$ pmd cpd --minimum-tokens 70 --language c --files projet.c
Found a 4 line (112 tokens) duplication in the following files:
Starting at line 43 of projet.c
Starting at line 79 of projet.c
void selection_ellipse(unsigned char image[HAUTEUR][LARGEUR],int C_x, int C_y, int a, int b, unsigned char selection[2*b][2*a]){
for (int y = 0; y <2*b; y++) { //balaie un rectangle de hauteur 2b et de largeur 2a (rectangle contenant l'ellipse)
for (int x = 0; x < 2*a; x++) {
if ( ( (pow((x-a),2))/(a*a) + (pow((y-b),2))/(b*b)) <= 1 ) //vérifie que la portion d'image est bien dans l'ellipse
#include
int main ()
{
int *p;
printf ("Val = %d\n", *p);
return 0;
}
$ gcc test_w.c
$ gcc test_w.c -W -Wall -Wextra
test_w.c:6:27: warning: variable 'p' is uninitialized when used here [-Wuninitialized]
printf("Val = %d\n", *p);
^
test_w.c:5:11: note: initialize the variable 'p' to silence this warning
int *p;
^
= NULL
1 warning generated.
allows to detect common mistakes
from one student project (SE3 2020-2021)
$ gcc negative.c
= no errors, no warnings
from one student project (SE3 2020-2021)
$ gcc negative.c -c -W -Wall -Wextra
negative.c:212:8: warning: unused variable 'imageMixe' [-Wunused-variable]
int imageMixe=sauvegardeImage("Image_Resultat.pgm",re);
^
negative.c:221:7: warning: unused variable 'imageMixe' [-Wunused-variable]
int imageMixe=sauvegardeImage("Image_Resultat.pgm",re);
^
negative.c:230:7: warning: unused variable 'imageMixe' [-Wunused-variable]
int imageMixe=sauvegardeImage("Image_Resultat.pgm",re);
^
negative.c:239:13: warning: unused variable 'imageMixe' [-Wunused-variable]
int imageMixe=sauvegardeImage("Image_Resultat.pgm",re);
^
negative.c:91:73: warning: unused variable 'charge6' [-Wunused-variable]
int charge5 = chargeImage("../images/fog-1535201_800.pgm",image5),charge6=chargeImage("../images/royalPalaceMadrid.pgm",image6);
^
negative.c:90:139: warning: unused variable 'magic1' [-Wunused-variable]
unsigned char image5[HAUTEUR][LARGEUR], image6[HAUTEUR][LARGEUR],copieMagic[HAUTEUR][LARGEUR]={0},copieMagic2[HAUTEUR][LARGEUR]={0},magic1[HAUTEUR][LARGEUR]={0},magic2[HAUTEUR]...
^
6 warnings generated.
= 6 warnings
from one student project (SE3 2020-2021)
$ clang-tidy --quiet -checks='*' negative.c --
negative.c:12:3: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:12:7: warning: variable 'j' is not initialized [cppcoreguidelines-init-variables]
negative.c:12:9: warning: variable 'i' is not initialized [cppcoreguidelines-init-variables]
negative.c:14:20: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:25:3: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:25:7: warning: variable 'i' is not initialized [cppcoreguidelines-init-variables]
negative.c:25:9: warning: variable 'j' is not initialized [cppcoreguidelines-init-variables]
negative.c:28:9: warning: narrowing conversion from 'float' to 'int' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:28:14: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:28:25: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:29:11: warning: narrowing conversion from 'float' to 'int' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:29:16: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:29:27: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:30:12: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:30:19: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:30:34: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:30:41: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:39:6: warning: function 'magicwand' is within a recursive call chain [misc-no-recursion]
negative.c:39:6: note: example recursive call chain, starting from function 'magicwand'
negative.c:47:6: note: Frame #1: function 'magicwand' calls function 'magicwand' here:
negative.c:47:6: note: ... which was the starting point of the recursive call chain; there may be other cycles
negative.c:41:26: warning: 800 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers]
negative.c:41:36: warning: 600 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers]
negative.c:42:12: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:42:29: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:44:14: warning: 255 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers]
negative.c:78:5: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:79:5: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:80:5: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:80:9: warning: variable 'x1' is not initialized [cppcoreguidelines-init-variables]
negative.c:80:12: warning: variable 'x2' is not initialized [cppcoreguidelines-init-variables]
negative.c:80:15: warning: variable 'y1' is not initialized [cppcoreguidelines-init-variables]
negative.c:80:18: warning: variable 'y2' is not initialized [cppcoreguidelines-init-variables]
negative.c:84:6: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:85:6: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:86:6: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:86:12: warning: variable 'a' is not initialized [cppcoreguidelines-init-variables]
negative.c:86:14: warning: variable 'b' is not initialized [cppcoreguidelines-init-variables]
negative.c:86:17: warning: variable 'xc' is not initialized [cppcoreguidelines-init-variables]
negative.c:86:20: warning: variable 'yc' is not initialized [cppcoreguidelines-init-variables]
negative.c:89:7: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:89:11: warning: variable 'x' is not initialized [cppcoreguidelines-init-variables]
negative.c:89:13: warning: variable 'y' is not initialized [cppcoreguidelines-init-variables]
negative.c:90:7: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:91:7: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:95:7: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:95:20: warning: variable 'jeu' is not initialized [cppcoreguidelines-init-variables]
negative.c:97:7: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:97:13: warning: variable 't' is not initialized [cppcoreguidelines-init-variables]
negative.c:97:16: warning: variable 'ih' is not initialized [cppcoreguidelines-init-variables]
negative.c:97:20: warning: variable 'ib' is not initialized [cppcoreguidelines-init-variables]
negative.c:98:7: warning: multiple declarations in a single statement reduces readability [readability-isolate-declaration]
negative.c:106:8: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
negative.c:114:4: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
negative.c:117:4: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
negative.c:120:4: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
negative.c:123:4: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
negative.c:128:30: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:129:32: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:134:17: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:146:6: warning: 'scanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead [cert-err34-c]
negative.c:149:6: warning: 'scanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead [cert-err34-c]
negative.c:152:6: warning: 'scanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead [cert-err34-c]
negative.c:155:6: warning: 'scanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead [cert-err34-c]
negative.c:161:33: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:162:37: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:167:21: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:178:3: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
negative.c:181:3: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
negative.c:184:3: warning: 'scanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead [cert-err34-c]
negative.c:186:8: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:187:8: warning: narrowing conversion from 'int' to 'float' [bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions]
negative.c:193:29: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:194:32: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:198:5: warning: misleading indentation: statement is indented too deeply [readability-misleading-indentation]
negative.c:193:3: note: did you mean this line to be inside this 'for'
negative.c:200:22: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:208:30: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:210:33: warning: 255 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers]
negative.c:217:29: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:218:31: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:226:29: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:227:31: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:236:35: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:237:30: warning: statement should be inside braces [google-readability-braces-around-statements,hicpp-braces-around-statements,readability-braces-around-statements]
negative.c:247:8: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
= 88 warnings !!!
They can also be included in your editors (vim, Sublime Text, Visual Studio...)
#include
int main (void)
{
char *buffer = malloc (2);
if (buffer != NULL)
{
buffer[0] = 'a';
buffer[1] = 'b';
free (buffer);
buffer[0] = 'a';
}
return 0;
}
$ clang -W -Wall -g -fsanitize=address use_mem.c
$ ./a.out
=================================================================
==65839==ERROR: AddressSanitizer: heap-use-after-free on address 0x6020000000f0 at pc 0x0001010b0f2b bp 0x7ffeeeb52740 sp 0x7ffeeeb52738
WRITE of size 1 at 0x6020000000f0 thread T0
#0 0x1010b0f2a in main use_mem.c:14
#1 0x7fff203a9620 in start+0x0 (libdyld.dylib:x86_64+0x15620)
0x6020000000f0 is located 0 bytes inside of 2-byte region [0x6020000000f0,0x6020000000f2)
freed by thread T0 here:
#0 0x1011082c6 in wrap_free+0xa6 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x492c6)
#1 0x1010b0edf in main use_mem.c:13
#2 0x7fff203a9620 in start+0x0 (libdyld.dylib:x86_64+0x15620)
previously allocated by thread T0 here:
#0 0x10110817d in wrap_malloc+0x9d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x4917d)
#1 0x1010b0e18 in main use_mem.c:6
#2 0x7fff203a9620 in start+0x0 (libdyld.dylib:x86_64+0x15620)
SUMMARY: AddressSanitizer: heap-use-after-free use_mem.c:14 in main
Shadow bytes around the buggy address:
0x1c03ffffffc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1c03ffffffd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1c03ffffffe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1c03fffffff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1c0400000000: fa fa fd fd fa fa 00 00 fa fa 00 07 fa fa 00 fa
=>0x1c0400000010: fa fa 00 04 fa fa 00 00 fa fa 00 06 fa fa[fd]fa
0x1c0400000020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x1c0400000060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
$ clang -W -Wall use_mem.c
$ valgrind ./a.out
==2936== Memcheck, a memory error detector
==2936== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2936== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==2936== Command: ./a.out
==2936==
==2936== Invalid write of size 1
==2936== at 0x401186: main (in /home/imaEns/jdequidt/a.out)
==2936== Address 0x4a41040 is 0 bytes inside a block of size 2 free'd
==2936== at 0x48369AB: free (vg_replace_malloc.c:530)
==2936== by 0x401181: main (in /home/imaEns/jdequidt/a.out)
==2936== Block was alloc'd at
==2936== at 0x483577F: malloc (vg_replace_malloc.c:299)
==2936== by 0x40115A: main (in /home/imaEns/jdequidt/a.out)
==2936==
==2936==
==2936== HEAP SUMMARY:
==2936== in use at exit: 0 bytes in 0 blocks
==2936== total heap usage: 1 allocs, 1 frees, 2 bytes allocated
==2936==
==2936== All heap blocks were freed -- no leaks are possible
==2936==
==2936== For counts of detected and suppressed errors, rerun with: -v
==2936== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
-g
to enable debug infoCommand | Shortcut | Comment |
run [args] | r | start the program |
quit | q | |
CTRL+C | break |
Command | Shortcut | Comment |
break [where] | b | where = line number or function |
disable # | # bp number | |
enable # (once) | # bp number | |
info break | informations |
Command | Shortcut | Comment |
watch (expr ou cond) | b | surveillance |
break # if cond | bp + watch | |
continue | c # | until bp # |
next # | n # | jump # lines |
step # | s # | jump # lines (goes inside func) |
finish | continue until func ends |
Command | Shortcut | Comment |
bt (full) | display stack | |
up / down | stack navigation | |
print expr | p expr | display expr |
|
$ size a.out
text data bss dec hex filename
1587 588 12 2187 88b a.out
int incr()
{
static int c=0;
return c++;
}
int func()
{
return incr();
}
int main()
{
int i = func();
return i;
}
$ gcc -g tt_gdb.c
$ lldb a.out
(lldb) target create "a.out"
(lldb) b incr
Breakpoint 1: where = a.out`incr + 4 at tt_gdb.c:4:11, address = 0x0000000100003f64
(lldb) run
Process 85292 launched: '/Users/jeremie/tmp/a.out' (x86_64)
Process 85292 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
frame #0: 0x0000000100003f64 a.out`incr at tt_gdb.c:4:11
1 int incr()
2 {
3 static int c=0;
-> 4 return c++;
5 }
6
7 int func()
Target 0: (a.out) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
* frame #0: 0x0000000100003f64 a.out`incr at tt_gdb.c:4:11
frame #1: 0x0000000100003f89 a.out`func at tt_gdb.c:9:10
frame #2: 0x0000000100003fa4 a.out`main at tt_gdb.c:14:11
frame #3: 0x00007fff203a9621 libdyld.dylib`start + 1
You need to test every functions you write (= unit testing) because
You need to be sure that your code improve over time and do not regress
Example
Ptcellule allouer ()
{
return ((Ptcellule)malloc (sizeof (Cellule)));
}
should return something != NULL
Example -- how to test
#include
// [...]
int main()
{
assert( NULL != allouer() )
return 0;
}
if everything is OK, no errors
otherwise Assertion 'NULL != allouer()' failed
Example -- limitations
you have to add lots of asserts that can hinders performances
Solution: is to build another executable for testing
Example -- check.h
small library that enables unit testing
int init_rn_generator ()
{
static unsigned int is_rn_seed_loaded = 0;
if (is_rn_seed_loaded == 0)
{
srand (time (NULL));
is_rn_seed_loaded++;
}
return is_rn_seed_loaded;
}
void init_array (int t[], unsigned int N, unsigned int V_MAX)
{
for (unsigned int i = 0; i < N; i++)
{
t[i] = rand () % V_MAX;
}
}
#include
START_TEST (test_rn)
{
ck_assert (init_rn_generator () == 1);
}
END_TEST
START_TEST (test_init)
{
const unsigned int V = 10;
const unsigned int T = 5;
int array[T];
init_array (array, T, V);
for (unsigned int i = 0; i < T; i++)
{
ck_assert (array[i] >= 0);
ck_assert (array[i] < (int)V);
}
}
END_TEST
Suite *sort_suite (void)
{
Suite *s = suite_create ("SortAlgorithms");
TCase *tc_core = tcase_create ("Core");
tcase_add_test (tc_core, test_rn);
tcase_add_test (tc_core, test_init);
suite_add_tcase (s, tc_core);
return s;
}
int main (void)
{
int no_failed = 0;
Suite * s = sort_suite ();
SRunner *runner = srunner_create (s);
srunner_run_all (runner, CK_NORMAL);
no_failed = srunner_ntests_failed (runner);
srunner_free (runner);
return (no_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
./build/unit_tests
Running suite(s): SortAlgorithms
100%: Checks: 2, Failures: 0, Errors: 0
you can add some options (lcov) to check the coverage of your tests
a complete exmaple is available on gitlab Ima_Prog_Adv
# Building rules
default: $(EXEC_APPLI)
# Main app
main: $(EXEC_APPLI)
# Unit Tests
tests: $(EXEC_TESTS) coverage
# Code formatting and Duplicates
lint: format pmd
# Static Checkers
checkers: tidy rats cppcheck
format:
@./scripts/run-clang-format.py -r $(SRC_DIR) $(INCL_DIR) $(TEST_DIR)
pmd:
pmd cpd --minimum-tokens 100 --language c --files $(SRC_DIR) \
$(TEST_DIR) $(INCL_DIR)
tidy:
clang-tidy --quiet -checks='*' -header-filter="^include" \
$(SRC_DIR)/*.c -- -I$(OS_DIR) -I$(INCL_DIR)
rats:
rats -l c -w 3 $(SRC_DIR)/*.c $(TEST_DIR)/*.c
cppcheck:
cppcheck --enable=warning,style,portability $(SRC_DIR)/*.c $(TEST_DIR)/*.c
coverage:
$(EXEC_TESTS)
gcov -f -b $(BUILD_DIR)/
lcov --directory $(BUILD_DIR) --base-directory $(BUILD_DIR) -c \
-o $(REPORT_DIR)/cov.info
genhtml $(REPORT_DIR)/cov.info -o $(REPORT_DIR)
stages:
- codestyling
- check
- build
- test
- coverage
- clean
job:codestyling:
stage: codestyling
script: ./scripts/run-clang-format.py -r src includes tests
job:check:tidy:
stage: check
script: clang-tidy src/*.c -- -Iincludes
when: always
job:check:cppcheck:
stage: check
script: cppcheck --enable=warning,style,portability src/*.c
when: always
job:build:
stage: build
script:
- mkdir build
- make main
when: always
artifacts:
paths:
- build/
job:test:
stage: test
script:
- make tests
- build/tri_selec_tests
when: on_success
artifacts:
paths:
- build/
job:coverage:
stage: coverage
script:
- llvm-cov gcov -f -b build/*
- lcov --directory build --base-directory . -c -o cov.info
- mkdir report
- genhtml cov.info -o report
coverage: '/^\s*lines\S*\s*(\d+(?:\.\d+)?%)\s*/'
when: on_success
dependencies:
- job:test
artifacts:
paths:
- report/
set nocompatible "be iMproved, required
filetype off "required
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
Plugin 'VundleVim/Vundle.vim' "Gestionnaire de paquets
Plugin 'w0rp/ale' "Syntax analyzer
Plugin 'universal-ctags/ctags' "Ctags pour symboles
Plugin 'taglist.vim' "gestion tags du code source
Plugin 'rhysd/vim-clang-format' "clang-format
call vundle#end() "required
filetype plugin indent on "required