crun 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +3 -0
- data/README.rdoc +36 -16
- data/bin/crun +155 -7
- data/examples/greeting_noheader.c +44 -0
- data/lib/parsec.rb +311 -0
- data/lib/version.rb +1 -1
- data/test/golden/autoheader.txt +12 -0
- data/test/result/autoheader.txt +12 -0
- data/test/test_crun.rb +1 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fdbb73ff120ce8d6c877f040acd9dae16b2ad0d3
|
4
|
+
data.tar.gz: 9b9273e32f0366ba3639c709741416e2d840e384
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b985a96db0b25032904b6c0089d0a915477f278d38e665f785d60c40018a7d34ad649604ece9222bd5052351a71e398dfd42862b63f78f0b691ac9fcaba4814
|
7
|
+
data.tar.gz: 22ebcd85b9ebdfeb88eabd3101093781183753d294634f057241f9cb2992c5ff66d4295bd588210022087236f911a95b89189a28ef5df7a001ef2c9e75a28edd
|
data/CHANGELOG.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -11,8 +11,8 @@
|
|
11
11
|
Crun is an utility for compiling and running C-programs straight from
|
12
12
|
C source files. Crun supports compile-run and compile-debug flows.
|
13
13
|
|
14
|
-
|
15
|
-
need, for example, an external library for the program.
|
14
|
+
User can embed custom compile options into the main c-file, if user
|
15
|
+
need, for example, an external library for the program. User can also
|
16
16
|
control the command line parameters passed to the program through
|
17
17
|
Crun.
|
18
18
|
|
@@ -22,14 +22,29 @@ the list of files. Crun is targeted for small C-programs, so it is not
|
|
22
22
|
typical to have multiple files.
|
23
23
|
|
24
24
|
User of Crun is most probably trying out some basic C-features or maybe
|
25
|
-
prototyping with some algorithm.
|
25
|
+
prototyping with some algorithm.
|
26
|
+
|
27
|
+
User can avoid listing stardard POSIX headers, if the Autoheader mode
|
28
|
+
is used. Autoheader mode will silently add "#include" directives to
|
29
|
+
the start of source files for user.
|
30
|
+
|
31
|
+
In Autoheader mode, the source files are examined for function
|
32
|
+
calls. The function calls that are part of POSIX, will get the
|
33
|
+
corresponding header files automatically included. Other libraries
|
34
|
+
(and includes) are not handled, since the possibility for ambiguous
|
35
|
+
function names are too high. Autoheader mode is activated with "-a"
|
36
|
+
option. Before Autoheader mode is used, user should update the
|
37
|
+
Autoheader DB:
|
38
|
+
|
39
|
+
shell> auto -u
|
40
|
+
|
26
41
|
|
27
42
|
|
28
43
|
= Example runs
|
29
44
|
|
30
|
-
In order to run "Hello World" example with Crun,
|
45
|
+
In order to run "Hello World" example with Crun, user need to have the
|
31
46
|
C source file with "Hello World" program in it. This is available in
|
32
|
-
the "examples" directory for
|
47
|
+
the "examples" directory for user.
|
33
48
|
|
34
49
|
"hello.c" content:
|
35
50
|
|
@@ -40,7 +55,7 @@ the "examples" directory for you.
|
|
40
55
|
return 0;
|
41
56
|
}
|
42
57
|
|
43
|
-
|
58
|
+
User can execute compile-run flow by:
|
44
59
|
|
45
60
|
shell> crun -f examples/hello.c
|
46
61
|
|
@@ -48,7 +63,7 @@ The output from program is:
|
|
48
63
|
|
49
64
|
Hello World!
|
50
65
|
|
51
|
-
|
66
|
+
User can execute compile-debug flow by:
|
52
67
|
|
53
68
|
shell> crun -f examples/hello.c -d
|
54
69
|
|
@@ -62,7 +77,7 @@ Check available command line interface, CLI, options for Crun:
|
|
62
77
|
|
63
78
|
= Options
|
64
79
|
|
65
|
-
|
80
|
+
User can specify options for Crun in multiple ways. Typically CLI is
|
66
81
|
used to control Crun, but if the program uses static compilation
|
67
82
|
options, those live most naturally in the source file itself.
|
68
83
|
|
@@ -96,7 +111,7 @@ Here is the complete list of options that control Crun:
|
|
96
111
|
[ compprog ]
|
97
112
|
|
98
113
|
Array including the compiler name and base options. This options
|
99
|
-
array should only have one entry, but in theory
|
114
|
+
array should only have one entry, but in theory user could have
|
100
115
|
multiple entries. The entries are concatenated with SPACE. The
|
101
116
|
compiler command has to have same command line parameters as
|
102
117
|
"gcc". Default is: ["gcc", "-Wall", "-std=c11"].
|
@@ -104,7 +119,7 @@ Here is the complete list of options that control Crun:
|
|
104
119
|
[ dbugprog ]
|
105
120
|
|
106
121
|
Array including the debugger name and base options. This options
|
107
|
-
array should only have one entry, but in theory
|
122
|
+
array should only have one entry, but in theory user could have
|
108
123
|
multiple entries. The entries are concatenated with SPACE. The
|
109
124
|
debugger command has to have same command line parameters as
|
110
125
|
"gdb". Default is: ["gdb", "--nx"].
|
@@ -138,7 +153,7 @@ User can specify static overrides in "$HOME/.crun" file and/or in
|
|
138
153
|
"-c" Crun CLI option to read in option overrides.
|
139
154
|
|
140
155
|
For example if the program is always run with "-c 10" command line
|
141
|
-
options,
|
156
|
+
options, user can set in ".crun":
|
142
157
|
|
143
158
|
@crun['progopts'] = [ "-c 10" ]
|
144
159
|
|
@@ -148,19 +163,22 @@ use the -o option:
|
|
148
163
|
shell> crun -o "progopts:=-c 10" ...
|
149
164
|
|
150
165
|
This sets the list of program options to "-c 10". ":=" syntax means
|
151
|
-
setting the Array of options. If
|
152
|
-
options,
|
166
|
+
setting the Array of options. If user needs to add to existing list of
|
167
|
+
options, perform:
|
153
168
|
|
154
169
|
shell> crun -o "progopts:+-c 10" ...
|
155
170
|
|
156
171
|
Thus ":+" is the syntax for appending more options to existing list.
|
157
172
|
|
173
|
+
If user wants to put an option to start of list, the ":." syntax is
|
174
|
+
used.
|
175
|
+
|
158
176
|
The simplest way to pass 'progopts' to target program is using the
|
159
177
|
"-p" CLI option. However user must be aware of the shell option
|
160
178
|
parsing, since it is very easy to miss the target program and actually
|
161
179
|
pass the option to Crun.
|
162
180
|
|
163
|
-
If
|
181
|
+
If user wants to pass '-' based option to target program:
|
164
182
|
|
165
183
|
shell> crun -p "\-c 10" ...
|
166
184
|
|
@@ -177,8 +195,10 @@ Summary of option setting from first to last:
|
|
177
195
|
* "-o" options from Crun CLI.
|
178
196
|
* "-p" options for program (only).
|
179
197
|
|
198
|
+
The options are applied in this order, and later options may override
|
199
|
+
the earlier settings, i.e. CLI options have the highest priority.
|
180
200
|
|
181
201
|
= Issues
|
182
202
|
|
183
|
-
If
|
184
|
-
|
203
|
+
If user takes C source input from STDIN, debug option can't be
|
204
|
+
used. Only compile-run flow is possible.
|
data/bin/crun
CHANGED
@@ -11,6 +11,10 @@ require 'como'
|
|
11
11
|
include Como
|
12
12
|
require 'tempfile'
|
13
13
|
require_relative '../lib/version'
|
14
|
+
require 'yaml'
|
15
|
+
|
16
|
+
# require 'byebug'
|
17
|
+
|
14
18
|
|
15
19
|
Spec.command( 'crun', 'Tero Isannainen', '2016',
|
16
20
|
[
|
@@ -18,14 +22,18 @@ Spec.command( 'crun', 'Tero Isannainen', '2016',
|
|
18
22
|
[ :opt_single, 'prog', '-p', "Add program options (i.e. progopts)." ],
|
19
23
|
[ :switch, 'debug', '-d', "Debug program." ],
|
20
24
|
[ :opt_multi, 'opts', '-o', "Set/add crun option." ],
|
25
|
+
[ :switch, 'auto', '-a', "Use Autoheader mode." ],
|
21
26
|
[ :opt_multi, 'conf', '-c', "Crun option file(s)." ],
|
22
27
|
[ :switch, 'input', '-i', "Read stdin for C-code." ],
|
23
28
|
[ :opt_single, 'save', '-s', "Save compiled file." ],
|
24
|
-
[ :exclusive, 'template','-t', "Output a template file for program." ],
|
25
29
|
[ :switch, 'verbose', '-v', "Verbose (to stderr)." ],
|
30
|
+
[ :switch, 'template','-t', "Output a template file for program." ],
|
31
|
+
[ :switch, 'database','-u', "Update DB for Autoheader mode." ],
|
26
32
|
] )
|
27
33
|
|
28
34
|
|
35
|
+
CRUN_HEADERS = "#{ENV['HOME']}/.crun_headers"
|
36
|
+
|
29
37
|
# Execute shell command with optional verbosity.
|
30
38
|
def crun_exe( cmd )
|
31
39
|
if Opt['verbose'].given
|
@@ -34,17 +42,28 @@ def crun_exe( cmd )
|
|
34
42
|
system( cmd )
|
35
43
|
end
|
36
44
|
|
45
|
+
|
37
46
|
# Parse and apply option setting.
|
38
47
|
def apply_opt( opt )
|
48
|
+
|
39
49
|
if opt.split(':=').length == 2
|
50
|
+
|
51
|
+
# Create option array.
|
40
52
|
parts = opt.split(':=')
|
41
53
|
@crun[parts[0]] = [ parts[1] ]
|
54
|
+
|
42
55
|
elsif opt.split(':+').length == 2
|
56
|
+
|
57
|
+
# Add to existing option array.
|
43
58
|
parts = opt.split(':+')
|
44
59
|
@crun[parts[0]].push parts[1]
|
60
|
+
|
45
61
|
elsif opt.split(':.').length == 2
|
62
|
+
|
63
|
+
# Put option to start of list.
|
46
64
|
parts = opt.split(':.')
|
47
65
|
@crun[parts[0]].unshift parts[1]
|
66
|
+
|
48
67
|
end
|
49
68
|
end
|
50
69
|
|
@@ -67,19 +86,135 @@ int main( int argc, char** argv )
|
|
67
86
|
end
|
68
87
|
|
69
88
|
|
89
|
+
if Opt['database'].given
|
90
|
+
require_relative '../lib/parsec'
|
91
|
+
|
92
|
+
posix_headers = %w{
|
93
|
+
<aio.h> <libgen.h> <spawn.h> <sys/time.h>
|
94
|
+
<arpa/inet.h> <limits.h> <stdarg.h> <sys/times.h>
|
95
|
+
<assert.h> <locale.h> <stdbool.h> <sys/types.h>
|
96
|
+
<complex.h> <math.h> <stddef.h> <sys/uio.h>
|
97
|
+
<cpio.h> <monetary.h> <stdint.h> <sys/un.h>
|
98
|
+
<ctype.h> <mqueue.h> <stdio.h> <sys/utsname.h>
|
99
|
+
<dirent.h> <ndbm.h> <stdlib.h> <sys/wait.h>
|
100
|
+
<dlfcn.h> <net/if.h> <string.h> <syslog.h>
|
101
|
+
<errno.h> <netdb.h> <strings.h> <tar.h>
|
102
|
+
<fcntl.h> <netinet/in.h> <stropts.h> <termios.h>
|
103
|
+
<fenv.h> <netinet/tcp.h> <sys/ipc.h> <tgmath.h>
|
104
|
+
<float.h> <nl_types.h> <sys/mman.h> <time.h>
|
105
|
+
<fmtmsg.h> <poll.h> <sys/msg.h> <trace.h>
|
106
|
+
<fnmatch.h> <pthread.h> <sys/resource.h> <ulimit.h>
|
107
|
+
<ftw.h> <pwd.h> <sys/select.h> <unistd.h>
|
108
|
+
<glob.h> <regex.h> <sys/sem.h> <utime.h>
|
109
|
+
<grp.h> <sched.h> <sys/shm.h> <utmpx.h>
|
110
|
+
<iconv.h> <search.h> <sys/socket.h> <wchar.h>
|
111
|
+
<inttypes.h> <semaphore.h> <sys/stat.h> <wctype.h>
|
112
|
+
<iso646.h> <setjmp.h> <sys/statvfs.h> <wordexp.h>
|
113
|
+
<langinfo.h> <signal.h>
|
114
|
+
}
|
115
|
+
|
116
|
+
func_header_map = {}
|
117
|
+
|
118
|
+
posix_headers.each do |header|
|
119
|
+
base = header[1..-2]
|
120
|
+
|
121
|
+
headername = "/usr/include/" + base
|
122
|
+
|
123
|
+
if File.exist?( headername )
|
124
|
+
file = ParseC::SourceFile.new( headername, "/usr/include" )
|
125
|
+
file.conf( :defsonly, true )
|
126
|
+
# file.conf( :mainonly, true )
|
127
|
+
file.conf( :mainonly, false )
|
128
|
+
file.parse
|
129
|
+
|
130
|
+
file.funcdefs.each do |k,v|
|
131
|
+
if k[0..1] != "__"
|
132
|
+
func_header_map[ k ] = header
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
File.write( CRUN_HEADERS, YAML.dump( func_header_map ) )
|
139
|
+
|
140
|
+
exit( false )
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# C-source file object.
|
145
|
+
class SourceFile
|
146
|
+
|
147
|
+
def initialize( name )
|
148
|
+
# Original name.
|
149
|
+
@name = name
|
150
|
+
|
151
|
+
# Current path to compile filed.
|
152
|
+
@path = name
|
153
|
+
|
154
|
+
# Temp file to remove (if any).
|
155
|
+
@remove = nil
|
156
|
+
end
|
157
|
+
|
158
|
+
def headerize
|
159
|
+
require_relative '../lib/parsec'
|
160
|
+
|
161
|
+
func_header_map = nil
|
162
|
+
if File.exist? CRUN_HEADERS
|
163
|
+
func_header_map = YAML.load( File.read( CRUN_HEADERS ) )
|
164
|
+
else
|
165
|
+
return
|
166
|
+
end
|
167
|
+
|
168
|
+
base = File.basename( @name, ".c" )
|
169
|
+
@path = "/dev/shm/auto-#{base}.c"
|
170
|
+
parse = ParseC::SourceFile.new( @name, File.dirname( @name ) )
|
171
|
+
parse.conf( :mainonly, true )
|
172
|
+
parse.parse
|
173
|
+
|
174
|
+
autoheaders = {}
|
175
|
+
|
176
|
+
parse.funcalls.each do |k,v|
|
177
|
+
if func_header_map[ k ]
|
178
|
+
autoheaders[ func_header_map[ k ] ] = true
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
fh = File.open( @path, "w" )
|
183
|
+
autoheaders.each do |k,v|
|
184
|
+
fh.write( "#include #{k}\n" )
|
185
|
+
end
|
186
|
+
fh.write( File.read( @name ) )
|
187
|
+
fh.close
|
188
|
+
|
189
|
+
# Set file to be removed.
|
190
|
+
@remove = @path
|
191
|
+
end
|
192
|
+
|
193
|
+
def path
|
194
|
+
@path
|
195
|
+
end
|
196
|
+
|
197
|
+
def delete
|
198
|
+
FileUtils.rm_f( @remove ) if @remove
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
|
70
204
|
if Opt['file'].given
|
71
|
-
cfiles = Opt['file'].value
|
205
|
+
cfiles = Opt['file'].value.map{ |i| SourceFile.new( i ) }
|
72
206
|
elsif Opt['input'].given
|
73
207
|
# Create a temp c-file for input.
|
74
208
|
input = STDIN.read
|
75
209
|
cfile = "#{Tempfile.new( "crun-source", '/dev/shm' ).path}.c"
|
76
210
|
File.write( cfile, input )
|
77
|
-
cfiles = [ cfile ]
|
211
|
+
cfiles = [ SourceFile.new( cfile ) ]
|
78
212
|
else
|
79
213
|
Spec.usage
|
80
214
|
end
|
81
215
|
|
82
216
|
|
217
|
+
|
83
218
|
# ------------------------------------------------------------
|
84
219
|
# Configurations:
|
85
220
|
|
@@ -126,7 +261,7 @@ end
|
|
126
261
|
compopts = @crun['compopts']
|
127
262
|
crun_re = /[\s]+crun-([a-z]+)/
|
128
263
|
|
129
|
-
File.readlines( cfiles[0] ).each do |line|
|
264
|
+
File.readlines( cfiles[0].path ).each do |line|
|
130
265
|
m = line.match( crun_re )
|
131
266
|
if m
|
132
267
|
option = m[1]
|
@@ -163,7 +298,7 @@ end
|
|
163
298
|
|
164
299
|
# ------------------------------------------------------------
|
165
300
|
# Create tempfile for executable.
|
166
|
-
exefile = File.basename( cfiles[0], '.c' )
|
301
|
+
exefile = File.basename( cfiles[0].path, '.c' )
|
167
302
|
exepath = Tempfile.new( "crun-#{exefile}", '/dev/shm' ).path
|
168
303
|
|
169
304
|
|
@@ -178,7 +313,16 @@ class Array
|
|
178
313
|
end
|
179
314
|
end
|
180
315
|
|
181
|
-
|
316
|
+
# Autoheader source files if needed.
|
317
|
+
if Opt['auto'].given
|
318
|
+
cfiles.each do |cfile|
|
319
|
+
cfile.headerize
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
cfile_paths = cfiles.map{ |i| i.path }
|
324
|
+
|
325
|
+
cmds.push "#{@crun['compprog'].crun} #{@crun['crunopts'].crun} #{@crun['compopts'].crun} #{cfile_paths.crun} -o #{exepath}"
|
182
326
|
|
183
327
|
# In order to avoid the "text file busy" issue, copy back and forth the exefile.
|
184
328
|
cmds.push "cp #{exepath} #{exepath}-tmp"
|
@@ -230,7 +374,11 @@ end
|
|
230
374
|
# ------------------------------------------------------------
|
231
375
|
# Always cleanup, but not necessary if using "/dev/shm".
|
232
376
|
if Opt['input'].given
|
233
|
-
system( "rm -f #{cfiles[0]}" )
|
377
|
+
system( "rm -f #{cfiles[0].name}" )
|
378
|
+
end
|
379
|
+
|
380
|
+
cfiles.each do |cfile|
|
381
|
+
cfile.delete
|
234
382
|
end
|
235
383
|
|
236
384
|
FileUtils.rm_f exepath
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/*
|
2
|
+
* Options for crun:
|
3
|
+
* -crun-compopts:=-O2
|
4
|
+
* crun-compopts:+-lm
|
5
|
+
* crun-compopts:.-std=c99
|
6
|
+
* crun-compopts:.-std=c11
|
7
|
+
* crun-progopts:=-c 12
|
8
|
+
*/
|
9
|
+
|
10
|
+
int main( int argc, char** argv )
|
11
|
+
{
|
12
|
+
char* greeting = "Greetings!\n";
|
13
|
+
char* greeting_n = "Greeting #%d!\n";
|
14
|
+
|
15
|
+
int strlenlog;
|
16
|
+
strlenlog = log2( strlen( greeting ) );
|
17
|
+
|
18
|
+
if ( strlenlog > 10 )
|
19
|
+
exit( 1 );
|
20
|
+
|
21
|
+
int times = 1;
|
22
|
+
if ( argc > 1 )
|
23
|
+
{
|
24
|
+
if ( !strcmp( argv[1], "-c" ) )
|
25
|
+
sscanf( argv[2], "%d", × );
|
26
|
+
else
|
27
|
+
{
|
28
|
+
fprintf( stderr, "Usage: greeting [-c count]\n" );
|
29
|
+
exit( 1 );
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
if ( times > 1 )
|
34
|
+
{
|
35
|
+
for ( int i = 0; i < times; i++ )
|
36
|
+
printf( greeting_n, i+1 );
|
37
|
+
}
|
38
|
+
else
|
39
|
+
{
|
40
|
+
printf( greeting );
|
41
|
+
}
|
42
|
+
|
43
|
+
return 0;
|
44
|
+
}
|
data/lib/parsec.rb
ADDED
@@ -0,0 +1,311 @@
|
|
1
|
+
# Detect suitable CLANG library
|
2
|
+
unless ENV['LIBCLANG']
|
3
|
+
begin
|
4
|
+
libdir = %x{llvm-config --libdir}.chomp
|
5
|
+
libs = Dir.glob( "#{libdir}/*libclang*so*" )
|
6
|
+
ENV['LIBCLANG'] = libs[0]
|
7
|
+
rescue
|
8
|
+
raise RuntimeError, "Could not find LLVM, try setting LIBCLANG to library!"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
# require 'ffi/clang' display stupid messages to stdout, silence them.
|
15
|
+
begin
|
16
|
+
require "stringio"
|
17
|
+
old_stdout, $stdout = $stdout, StringIO.new
|
18
|
+
require 'ffi/clang'
|
19
|
+
$stdout = old_stdout
|
20
|
+
end
|
21
|
+
|
22
|
+
# C-parsing module based on libclang and ffi/clang, i.e. ffi bindings.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# require 'parsec'
|
26
|
+
# ast = ParseC::SourceFile.new( './code_out.c', '.' )
|
27
|
+
#
|
28
|
+
module ParseC
|
29
|
+
|
30
|
+
# Source file contains C-function definitions and header file
|
31
|
+
# inclusions.
|
32
|
+
class SourceFile
|
33
|
+
|
34
|
+
# File name.
|
35
|
+
attr_reader :name
|
36
|
+
|
37
|
+
def SourceFile.parseLocalDefs( name, headerPath )
|
38
|
+
file = SourceFile.new( name, headerPath )
|
39
|
+
file.conf( :defsonly, true )
|
40
|
+
file.conf( :mainonly, true )
|
41
|
+
file.parse
|
42
|
+
file
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# Set file to parse and path for header search. Finally,
|
47
|
+
# perform parsing for the file.
|
48
|
+
def initialize( name, headerPath )
|
49
|
+
@name = name
|
50
|
+
@headerPath = headerPath
|
51
|
+
|
52
|
+
@localHeader = {}
|
53
|
+
@funcdefs = {}
|
54
|
+
@funcalls = {}
|
55
|
+
|
56
|
+
@conf = {}
|
57
|
+
@conf[ :defsonly ] = false
|
58
|
+
@conf[ :mainonly ] = false
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def conf( opt, value = nil )
|
63
|
+
if value
|
64
|
+
@conf[ opt ] = value
|
65
|
+
else
|
66
|
+
@conf[ opt ]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# Return list of included (local) header files. Can be used e.g. for
|
72
|
+
# c-to-h file dependence.
|
73
|
+
#
|
74
|
+
# @return [Array<HeaderFile>] Headers.
|
75
|
+
def headers
|
76
|
+
@localHeader
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Return list of defined functions. Can be used e.g. to
|
81
|
+
# generate function declarations to a header file
|
82
|
+
# (automatically).
|
83
|
+
#
|
84
|
+
# @return [Array<HeaderFile>] Functions.
|
85
|
+
def funcdefs
|
86
|
+
@funcdefs
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def funcalls
|
91
|
+
@funcalls
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Return true if file name is for a local header.
|
96
|
+
def isLocalHeader?( file )
|
97
|
+
if file[0] != '/' && /\.h$/.match( file )
|
98
|
+
true
|
99
|
+
else
|
100
|
+
false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# Parse c-file.
|
106
|
+
#
|
107
|
+
# @param headerPath [String] Path for system header lookup.
|
108
|
+
def parse( headerPath = @headerPath )
|
109
|
+
|
110
|
+
index = FFI::Clang::Index.new
|
111
|
+
translation_unit = index.parse_translation_unit( @name, headerPath )
|
112
|
+
cursor = translation_unit.cursor
|
113
|
+
|
114
|
+
# Get list of included local headers.
|
115
|
+
translation_unit.inclusions do |header|
|
116
|
+
if isLocalHeader? header
|
117
|
+
@localHeader[ header ] = HeaderFile.new( header )
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Recursively travel the c-file hierarchy to detect
|
122
|
+
# function definitions.
|
123
|
+
cursor.visit_children do |cursor, parent|
|
124
|
+
|
125
|
+
if cursor.kind == :cursor_function
|
126
|
+
|
127
|
+
# Check that function is in the file that is being
|
128
|
+
# analysed. Might also be from an included header.
|
129
|
+
if ( @conf[:mainonly] == false ) || cursor.location.from_main_file?
|
130
|
+
|
131
|
+
f = Function.new( cursor.spelling, cursor.variadic? )
|
132
|
+
f.setReturnType( cursor.result_type.spelling )
|
133
|
+
|
134
|
+
# Get argument list.
|
135
|
+
cursor.visit_children do |cursor, parent|
|
136
|
+
|
137
|
+
case cursor.kind
|
138
|
+
|
139
|
+
when :cursor_parm_decl
|
140
|
+
f.addArgument( cursor.spelling, cursor.type.spelling )
|
141
|
+
|
142
|
+
else
|
143
|
+
# puts cursor.kind
|
144
|
+
true
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
next :continue
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
# Mark function complete and add to list.
|
153
|
+
@funcdefs[ f.name ] = f.done
|
154
|
+
|
155
|
+
if @conf[:defsonly]
|
156
|
+
# Don't recurse anymore.
|
157
|
+
next :continue
|
158
|
+
else
|
159
|
+
next :recurse
|
160
|
+
end
|
161
|
+
|
162
|
+
else
|
163
|
+
|
164
|
+
next :continue
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
elsif cursor.kind == :cursor_call_expr
|
169
|
+
|
170
|
+
|
171
|
+
# if cursor.location.from_main_file?
|
172
|
+
f = FunCall.new( cursor.spelling )
|
173
|
+
@funcalls[ f.name ] = f
|
174
|
+
# end
|
175
|
+
|
176
|
+
next :recurse
|
177
|
+
|
178
|
+
else
|
179
|
+
|
180
|
+
# Non-functions are not interesting.
|
181
|
+
next :recurse
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
# Display c-file content.
|
190
|
+
def display
|
191
|
+
puts "File: #{@name}"
|
192
|
+
puts " Includes:"
|
193
|
+
@localHeader.each_value do |i|
|
194
|
+
puts " #{i.name} | #{i.path}"
|
195
|
+
end
|
196
|
+
puts " Functions:"
|
197
|
+
@funcdefs.each_value do |f|
|
198
|
+
puts " #{f.declaration}"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
# Show raw content of c-file.
|
204
|
+
def dump( headerPath = @headerPath )
|
205
|
+
index = FFI::Clang::Index.new
|
206
|
+
translation_unit = index.parse_translation_unit( @name, headerPath )
|
207
|
+
cursor = translation_unit.cursor
|
208
|
+
cursor.visit_children do |cursor, parent|
|
209
|
+
puts "#{cursor.kind} #{cursor.spelling}"
|
210
|
+
next :recurse
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
# Included local header.
|
219
|
+
class HeaderFile
|
220
|
+
|
221
|
+
# File name.
|
222
|
+
attr_reader :name
|
223
|
+
|
224
|
+
# File path.
|
225
|
+
attr_reader :path
|
226
|
+
|
227
|
+
# Initialize.
|
228
|
+
def initialize( path )
|
229
|
+
@name = File.basename( path )
|
230
|
+
@path = path
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
# Defined function.
|
237
|
+
class Function
|
238
|
+
|
239
|
+
# Func name.
|
240
|
+
attr_reader :name
|
241
|
+
|
242
|
+
# Variadic (end with ellipsis).
|
243
|
+
attr_reader :variadic
|
244
|
+
|
245
|
+
|
246
|
+
# Initialize function object. Default return type to void.
|
247
|
+
#
|
248
|
+
# @param name [String] Func name.
|
249
|
+
def initialize( name, variadic = false )
|
250
|
+
@name = name
|
251
|
+
@variadic = variadic
|
252
|
+
@args = {}
|
253
|
+
@returnType = 'void'
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
# Set function return type.
|
258
|
+
def setReturnType( type )
|
259
|
+
@returnType = type
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
# Add argument to functions argument list.
|
264
|
+
def addArgument( name, type )
|
265
|
+
@args[ name ] = type
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
# Close function defition. If argument list is empty, then arg
|
270
|
+
# list contains only void.
|
271
|
+
def done
|
272
|
+
if @variadic
|
273
|
+
@args[ nil ] = '...'
|
274
|
+
end
|
275
|
+
|
276
|
+
if @args.empty?
|
277
|
+
@args = { nil => 'void' }
|
278
|
+
end
|
279
|
+
self
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
# Get argument declarations String presentation.
|
284
|
+
def argDecl( name )
|
285
|
+
if name
|
286
|
+
# Has type.
|
287
|
+
"#{@args[ name ]} #{name}"
|
288
|
+
else
|
289
|
+
# Void/variadic.
|
290
|
+
@args[ name ]
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
# Return functions declation as String.
|
296
|
+
def declaration
|
297
|
+
"#{@returnType} #{@name}( #{(@args.keys.map { |k| argDecl(k) }).join(', ')} );"
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
class FunCall
|
303
|
+
# Func name.
|
304
|
+
attr_reader :name
|
305
|
+
|
306
|
+
def initialize( name )
|
307
|
+
@name = name
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
data/lib/version.rb
CHANGED
data/test/test_crun.rb
CHANGED
@@ -39,5 +39,6 @@ class CrunTest < Test::Unit::TestCase
|
|
39
39
|
def test_override2() runTest( "override2", "-f ../examples/greeting.c -o \"progopts:.-c 13\"" ); end
|
40
40
|
def test_override3() runTest( "override3", "-f ../examples/greeting.c -o \"progopts:+-c 13\"" ); end
|
41
41
|
def test_template() runTest( "template", "-f ../examples/greeting.c -o \"progopts:+-c 13\" -t" ); end
|
42
|
+
def test_autoheader() runTest( "autoheader", "-a -f ../examples/greeting_noheader.c" ); end
|
42
43
|
|
43
44
|
end
|
metadata
CHANGED
@@ -1,19 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crun
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tero Isannainen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: como
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.2'
|
17
20
|
- - ">="
|
18
21
|
- !ruby/object:Gem::Version
|
19
22
|
version: 0.2.0
|
@@ -21,9 +24,32 @@ dependencies:
|
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.2'
|
24
30
|
- - ">="
|
25
31
|
- !ruby/object:Gem::Version
|
26
32
|
version: 0.2.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: ffi-clang
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.3'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.3.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.3'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.3.0
|
27
53
|
description: |-
|
28
54
|
Crun is an utility for compiling and running C-programs straight from
|
29
55
|
C source files. Crun supports compile-run and compile-debug flows.
|
@@ -46,15 +72,19 @@ files:
|
|
46
72
|
- bin/crun
|
47
73
|
- examples/crun_conf.rb
|
48
74
|
- examples/greeting.c
|
75
|
+
- examples/greeting_noheader.c
|
49
76
|
- examples/hello.c
|
50
77
|
- examples/sizeof.c
|
78
|
+
- lib/parsec.rb
|
51
79
|
- lib/version.rb
|
80
|
+
- test/golden/autoheader.txt
|
52
81
|
- test/golden/basic.txt
|
53
82
|
- test/golden/conf.txt
|
54
83
|
- test/golden/override.txt
|
55
84
|
- test/golden/override2.txt
|
56
85
|
- test/golden/override3.txt
|
57
86
|
- test/golden/template.txt
|
87
|
+
- test/result/autoheader.txt
|
58
88
|
- test/result/basic.txt
|
59
89
|
- test/result/conf.txt
|
60
90
|
- test/result/override.txt
|