crun 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|