compiler: rework flags & support win path spaces + more

pull/1881/head
joe-conigliaro 2019-09-06 22:12:04 +10:00 committed by Alexander Medvednikov
parent e8068b5e1d
commit 8ac0a2b2dd
7 changed files with 201 additions and 185 deletions

View File

@ -10,6 +10,9 @@ import (
)
fn (v mut V) cc() {
// build any thirdparty obj files
v.build_thirdparty_obj_files()
// Just create a c file and exit
if v.out_name.ends_with('.c') {
os.mv(v.out_name_c, v.out_name)
@ -33,15 +36,6 @@ fn (v mut V) cc() {
v.log('cc() isprod=$v.pref.is_prod outname=$v.out_name')
mut a := [v.pref.cflags, '-std=gnu11', '-w'] // arguments for the C compiler
mut seenflags := map[string]int
mut uniqueflags := []string
for f in v.table.flags {
seenflags[ f ] = seenflags[ f ] + 1
if seenflags[ f ] > 1 { continue }
uniqueflags << f
}
flags := uniqueflags.join(' ')
// Set out name
if v.pref.is_so {
a << '-shared -fPIC '// -Wl,-z,defs'
v.out_name = v.out_name + '.so'
@ -92,7 +86,7 @@ fn (v mut V) cc() {
// -I flags
/*
mut args := ''
for flag in v.table.flags {
for flag in v.get_os_cflags() {
if !flag.starts_with('-l') {
args += flag
args += ' '
@ -133,7 +127,9 @@ mut args := ''
if v.os == .mac {
a << '-mmacosx-version-min=10.7'
}
a << flags
for flag in v.get_os_cflags() {
a << flag.format()
}
a << libs
// macOS code can include objective C TODO remove once objective C is replaced with C
// Without these libs compilation will fail on Linux
@ -224,9 +220,9 @@ fn (c mut V) cc_windows_cross() {
}
mut args := '-o $c.out_name -w -L. '
// -I flags
for flag in c.table.flags {
if !flag.starts_with('-l') {
args += flag
for flag in c.get_os_cflags() {
if flag.name != '-l' {
args += flag.format()
args += ' '
}
}
@ -243,9 +239,9 @@ fn (c mut V) cc_windows_cross() {
}
args += ' $c.out_name_c '
// -l flags (libs)
for flag in c.table.flags {
if flag.starts_with('-l') {
args += flag
for flag in c.get_os_cflags() {
if flag.name == '-l' {
args += flag.format()
args += ' '
}
}
@ -289,6 +285,19 @@ fn (c mut V) cc_windows_cross() {
println('Done!')
}
fn (c V) build_thirdparty_obj_files() {
for flag in c.get_os_cflags() {
if flag.value.ends_with('.o') {
if c.os == .msvc {
build_thirdparty_obj_file_with_msvc(flag.value)
}
else {
build_thirdparty_obj_file(flag.value)
}
}
}
}
fn find_c_compiler() string {
args := env_vflags_and_os_args().join(' ')
defaultcc := find_c_compiler_default()
@ -306,10 +315,6 @@ fn find_c_compiler_default() string {
}
fn find_c_compiler_thirdparty_options() string {
$if windows { return '' }
$if windows { return '' }
return '-fPIC'
}

115
compiler/cflags.v 100644
View File

@ -0,0 +1,115 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module main
import os
// C flag
struct CFlag{
os string // eg. windows | darwin | linux
name string // eg. -I
value string // eg. /path/to/incude
}
// check if cflag is in table
fn (table &Table) has_cflag(cflag CFlag) bool {
for cf in table.cflags {
if cf.os == cflag.os && cf.name == cflag.name && cf.value == cflag.value {
return true
}
}
return false
}
// get flags for current os
fn (v V) get_os_cflags() []CFlag {
mut flags := []CFlag
for flag in v.table.cflags {
if flag.os == ''
|| (flag.os == 'linux' && v.os == .linux)
|| (flag.os == 'darwin' && v.os == .mac)
|| (flag.os == 'windows' && (v.os == .windows || v.os == .msvc)) {
flags << flag
}
}
return flags
}
// format flag
fn (cf &CFlag) format() string {
mut value := cf.value
// convert to absolute path
if cf.name == '-I' || cf.name == '-L' || value.ends_with('.o') {
value = '"'+os.realpath(value)+'"'
}
return '$cf.name $value'.trim_space()
}
// parse the flags to []CFlag
// Note: clean up big time (joe-c)
fn (table mut Table) parse_cflag(cflag string) {
allowed_flags := [
'framework',
'library',
'I', 'l', 'L',
]
mut flag := cflag.trim_space()
if flag == '' {
return
}
mut fos := ''
mut name := ''
if flag.starts_with('linux') || flag.starts_with('darwin') || flag.starts_with('windows') {
pos := flag.index(' ')
fos = flag.left(pos).trim_space()
flag = flag.right(pos).trim_space()
}
for {
mut index := -1
mut value := ''
if flag[0] == `-` {
for f in allowed_flags {
i := 1+f.len
if i < flag.len && f == flag.substr(1,i) {
name = flag.left(i).trim_space()
flag = flag.right(i).trim_space()
break
}
}
}
for i in [flag.index(' '), flag.index(',')] {
if index == -1 || (i != -1 && i < index) {
index = i
}
} if index != -1 && flag[index] == ` ` && flag[index+1] == `-` {
for f in allowed_flags {
i := index+f.len
if i < flag.len && f == flag.substr(index, i) {
index = i
break
}
}
value = flag.left(index).trim_space()
flag = flag.right(index).trim_space()
} else if index != -1 && index < flag.len-2 && flag[index] == `,` {
value = flag.left(index).trim_space()
flag = flag.right(index+1).trim_space()
} else {
value = flag.trim_space()
index = -1
}
cf := CFlag{
os: fos,
name: name,
value: value
}
if !table.has_cflag(cf) {
table.cflags << cf
}
if index == -1 {
break
}
}
}

View File

@ -240,13 +240,13 @@ fn (g mut CGen) add_to_main(s string) {
}
fn build_thirdparty_obj_file(flag string) {
obj_path := flag.all_after(' ')
fn build_thirdparty_obj_file(path string) {
obj_path := os.realpath(path)
if os.file_exists(obj_path) {
return
}
println('$obj_path not found, building it...')
parent := os.dir( obj_path )
parent := os.dir(obj_path)
files := os.ls(parent)
mut cfiles := ''
for file in files {
@ -281,15 +281,15 @@ fn os_name_to_ifdef(name string) string {
}
fn platform_postfix_to_ifdefguard(name string) string {
switch name {
case '.v': return '' // no guard needed
case '_win.v': return '#ifdef _WIN32'
case '_nix.v': return '#ifndef _WIN32'
case '_lin.v': return '#ifdef __linux__'
case '_mac.v': return '#ifdef __APPLE__'
}
cerror('bad platform_postfix "$name"')
return ''
switch name {
case '.v': return '' // no guard needed
case '_win.v': return '#ifdef _WIN32'
case '_nix.v': return '#ifndef _WIN32'
case '_lin.v': return '#ifdef __linux__'
case '_mac.v': return '#ifdef __APPLE__'
}
cerror('bad platform_postfix "$name"')
return ''
}
// C struct definitions, ordered
@ -383,4 +383,3 @@ fn sort_structs(types []Type) []Type {
}
return types_sorted
}

View File

@ -141,46 +141,11 @@ fn (p mut Parser) chash() {
is_sig := p.is_sig()
if hash.starts_with('flag ') {
mut flag := hash.right(5)
// No the right os? Skip!
// mut ok := true
if hash.contains('linux') && p.os != .linux {
return
}
else if hash.contains('darwin') && p.os != .mac {
return
}
else if hash.contains('windows') && (p.os != .windows && p.os != .msvc) {
return
}
// Remove "linux" etc from flag
if flag.contains('linux') || flag.contains('darwin') || flag.contains('windows') {
pos := flag.index(' ')
flag = flag.right(pos)
}
has_vroot := flag.contains('@VROOT')
flag = flag.trim_space().replace('@VROOT', p.vroot)
if p.table.flags.contains(flag) {
return
}
// expand `@VMOD/pg/pg.o` to absolute path
has_vmod := flag.contains('@VMOD')
flag = flag.trim_space().replace('@VMOD', ModPath)
if p.table.flags.contains(flag) {
return
}
// expand `@VROOT` `@VMOD` to absolute path
flag = flag.replace('@VROOT', p.vroot)
flag = flag.replace('@VMOD', ModPath)
p.log('adding flag "$flag"')
// `@VROOT/thirdparty/glad/glad.o`, make sure it exists, otherwise build it
if (has_vroot || has_vmod) && flag.contains('.o') {
flag = os.realpath( flag )
//println( 'absolute filepath to objectfile is now: $flag | os is: $p.os ')
if p.os == .msvc {
build_thirdparty_obj_file_with_msvc(flag)
}
else {
build_thirdparty_obj_file(flag)
}
}
p.table.flags << flag
p.table.parse_cflag(flag)
return
}
if hash.starts_with('include') {

View File

@ -325,7 +325,9 @@ fn (v mut V) compile() {
cgen.save()
if v.pref.is_verbose {
v.log('flags=')
println(v.table.flags)
for flag in v.get_os_cflags() {
println(' * ' + flag.format())
}
}
v.cc()
}
@ -437,7 +439,7 @@ fn (v V) run_compiled_executable_and_exit() {
if v.pref.is_verbose {
println('============ running $v.out_name ============')
}
mut cmd := final_target_out_name(v.out_name).replace('.exe','')
mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe','') + '"'
if os.args.len > 3 {
cmd += ' ' + os.args.right(3).join(' ')
}
@ -781,9 +783,7 @@ fn new_v(args[]string) &V {
vroot := os.dir(os.executable())
//println('VROOT=$vroot')
// v.exe's parent directory should contain vlib
if os.dir_exists(vroot) && os.dir_exists(vroot + '/vlib/builtin') {
} else {
if !os.dir_exists(vroot) || !os.dir_exists(vroot + '/vlib/builtin') {
println('vlib not found. It should be next to the V executable. ')
println('Go to https://vlang.io to install V.')
exit(1)

View File

@ -207,11 +207,6 @@ fn find_msvc() ?MsvcResult {
}
}
struct ParsedFlag {
f string
arg string
}
pub fn (v mut V) cc_msvc() {
r := find_msvc() or {
// TODO: code reuse
@ -305,99 +300,41 @@ pub fn (v mut V) cc_msvc() {
mut lib_paths := []string{}
mut other_flags := []string{}
// Emily:
// this is a hack to try and support -l -L and object files
// passed on the command line
mut seenflags := map[string]int // no need to add the same flags more than once
for f in v.table.flags {
seenflags[ f ] = seenflags[ f ] + 1
if seenflags[ f ] > 1 { continue }
// People like to put multiple flags per line (which really complicates things)
// ...so we need to handle that
mut rest := f
for flag in v.get_os_cflags() {
mut arg := flag.value
//println('fl: $flag.name | flag arg: $arg')
mut flags := []ParsedFlag{}
for {
mut base := rest
fl := if rest.starts_with('-') {
base = rest.right(2).trim_space()
rest.left(2)
} else {
''
}
// Which ever one of these is lowest we use
// TODO: we really shouldnt support all of these cmon
mut lowest := base.index('-')
// dont break paths with hyphens
if lowest != 0 {
lowest = -1
}
for x in [base.index(' '), base.index(',')] {
if (x < lowest && x != -1) || lowest == -1 {
lowest = x
}
}
arg := if lowest != -1 {
rest = base.right(lowest).trim_space().trim(',')
base.left(lowest).trim_space().trim(',')
} else {
rest = ''
base.trim_space()
}
flags << ParsedFlag {
fl, arg
}
if rest.len == 0 {
break
// We need to see if the flag contains -l
// -l isnt recognised and these libs will be passed straight to the linker
// by the compiler
if flag.name == '-l' {
if arg.ends_with('.dll') {
cerror('MSVC cannot link against a dll (`#flag -l $arg`)')
}
// MSVC has no method of linking against a .dll
// TODO: we should look for .defs aswell
lib_lib := arg + '.lib'
real_libs << lib_lib
}
for flag in flags {
fl := flag.f
mut arg := flag.arg
if fl == '-I' || fl == '-L' {
arg = os.realpath( arg )
}
//println('fl: $fl | flag arg: $arg')
// We need to see if the flag contains -l
// -l isnt recognised and these libs will be passed straight to the linker
// by the compiler
if fl == '-l' {
if arg.ends_with('.dll') {
cerror('MSVC cannot link against a dll (`#flag -l $arg`)')
}
// MSVC has no method of linking against a .dll
// TODO: we should look for .defs aswell
lib_lib := arg + '.lib'
real_libs << lib_lib
}
else if fl == '-I' {
inc_paths << ' -I "$arg" '
}
else if fl == '-L' {
lpath := f.right(2).trim_space()
lib_paths << lpath
lib_paths << lpath + os.PathSeparator + 'msvc'
// The above allows putting msvc specific .lib files in a subfolder msvc/ ,
// where gcc will NOT find them, but cl will do...
// NB: gcc is smart enough to not need .lib files at all in most cases, the .dll is enough.
// When both a msvc .lib file and .dll file are present in the same folder,
// as for example for glfw3, compilation with gcc would fail.
}
else if arg.ends_with('.o') {
// msvc expects .obj not .o
other_flags << arg + 'bj'
}
else {
other_flags << arg
}
else if flag.name == '-I' {
inc_paths << ' ' + flag.format() + ' '
}
else if flag.name == '-L' {
lpath := flag.value
lib_paths << '"' + lpath + '"'
lib_paths << '"' + lpath + os.PathSeparator + 'msvc' + '"'
// The above allows putting msvc specific .lib files in a subfolder msvc/ ,
// where gcc will NOT find them, but cl will do...
// NB: gcc is smart enough to not need .lib files at all in most cases, the .dll is enough.
// When both a msvc .lib file and .dll file are present in the same folder,
// as for example for glfw3, compilation with gcc would fail.
}
else if flag.value.ends_with('.o') {
other_flags << flag.format().replace('.o', '.obj')
}
else {
other_flags << arg
}
}
// Include the base paths
@ -463,18 +400,16 @@ pub fn (v mut V) cc_msvc() {
os.rm(out_name_obj)
}
fn build_thirdparty_obj_file_with_msvc(flag string) {
fn build_thirdparty_obj_file_with_msvc(path string) {
msvc := find_msvc() or {
println('Could not find visual studio')
return
}
mut obj_path := flag.all_after(' ')
// msvc expects .obj not .o
mut obj_path := '${path}bj'
if obj_path.ends_with('.o') {
// msvc expects .obj not .o
obj_path = obj_path + 'bj'
}
obj_path = os.realpath(obj_path)
if os.file_exists(obj_path) {
println('$obj_path already build.')
@ -482,14 +417,14 @@ fn build_thirdparty_obj_file_with_msvc(flag string) {
}
println('$obj_path not found, building it (with msvc)...')
parent := os.dir( obj_path )
parent := os.dir(obj_path)
files := os.ls(parent)
mut cfiles := ''
for file in files {
if file.ends_with('.c') {
cfiles += '"' + os.realpath( parent + os.PathSeparator + file ) + '" '
}
}
}
include_string := '-I "$msvc.ucrt_include_path" -I "$msvc.vs_include_path" -I "$msvc.um_include_path" -I "$msvc.shared_include_path"'

View File

@ -17,7 +17,7 @@ mut:
modules []string // List of all modules registered by the application
imports []string // List of all imports
file_imports []FileImportTable // List of imports for file
flags []string // ['-framework Cocoa', '-lglfw3']
cflags []CFlag // ['-framework Cocoa', '-lglfw3']
fn_cnt int //atomic
obfuscate bool
}
@ -78,8 +78,6 @@ mut:
scope_level int
}
struct Type {
mut:
mod string
@ -106,7 +104,6 @@ struct TypeNode {
typ Type
}
// For debugging types
fn (t Type) str() string {
mut s := 'type "$t.name" {'