-
-
Notifications
You must be signed in to change notification settings - Fork 614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft for core:build. #2874
Draft for core:build. #2874
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to the comments in review I'd like to ask you to maybe document this package in odin doc style comments. It would be helpful to start documenting packages beyond mere function prototypes..
core/build/build.odin
Outdated
import "core:sync" | ||
import "core:encoding/json" | ||
|
||
syscall :: proc(cmd: string, echo: bool) -> int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syscall
associates with x86 instruction to call into kernel functions... maybe call this shell_exec
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change the name, thanks. I was planning to document the package of course, but I wanted to get it going first and get some feedback on the general API
core/build/private.odin
Outdated
_utf8_peek :: proc(bytes: string) -> (c: rune, size: int, ok: bool) { | ||
c, size = utf8.decode_rune_in_string(bytes) | ||
|
||
ok = c != utf8.RUNE_ERROR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You seem to be using tabs throughout your project, this line contains two spaces, which looks misaligned to me. Don't thank me :)
Actually looking closely some parts of your code use tabs while other use spaces... you gotta figure it out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hint: Try selecting line 17 and line 19 in github, the selection increments should help you tell spaces and tabs apart, in case you set tab width to 4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could u have a quick look to see if the tab problem was fixed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks correct. By the way, which text editor are you using? If it's supported maybe there are options to display invisible characters. I know sublime displays invisible characters when you select text, that sort of functionality may be useful if you're working with projects that have different tab/space rules and you're not sure if your editor messes it up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
im using vscode. I haven't been paying much attention to tabs/spaces but it seemed that i was able to just convert the entire file to tabs
config.out_file = "demo.exe" if target.platform.os == .Windows else "demo.out" | ||
config.out_dir = strings.concatenate({"out/", target.name}) | ||
config.build_mode = .EXE | ||
config.src_path = "src" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I'm aware this is a required option. Maybe print error when this isn't specified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error handling is still dumb. Will start making the code more friendly in that regard
General comments:
|
Thanks for the reviews. I'll get to fixing some of the issues mentioned.
|
From what I've seen I don't see that there's a cross-platform implementation. I suggest you hold that improvement in your head until the other packages catch up. Since there is work being done in those areas, maybe it's best not to spend time reimplementing things already being worked on. Relating to configs, maybe it's best to avoid directly feeding the build with the parsed configs but rather provide user an API to use the configs? I'm not sure whether it's a good idea to provide the interface to build system on behalf of the users since if they're using a build system they want something custom anyway. Can you explain the meaning behind these configs and why they're being added? |
@flysand7 the config is what's used by To explain configs more, a Let me know if this clarifies things. |
Yes I understand now. I might have misunderstood what you meant by the config |
I'll get back to working on this today. Are there any significant issues that would need to be resolved for this PR? My plan right now is to add the dependency feature and handle errors better. @gingerBill what do you propose for |
…oved parse_args. Added Settings.custom_args and optional allow_custom_args bool in settings_init_from_args. Added a custom default context that initializes a console logger. Added logging in favor of eprintf error handling. Needs review
…for easier replacement
I have made some modifications in response to the reviews. I added better error handling with return vals for most procs (some might still be missing, I'll keep working on it). I'm experimenting with using the I also added the option for custom flags as Bill suggested.
The API is mostly stable now. I'm still unsure about passing the settings to the I'd also want to know if the error handling+ error message handling is ok now or I should try something different? I'm really unsure about requiring the user the make the context in order for the logs to show up. It might be the best to just return an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the switch to log
. Allows the developer to configure the logging level and potentially handle the errors themselves, maybe avoid printing extra lines and focus on success state.
core/build/types.odin
Outdated
|
||
// Static lib? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Called archive (as in, object archive), it just contains a collection of obj files. Though I doubt people would recognize it by that name. I don't think it's worth it: Odin isn't supposed to be built from multiple modules and then archived/linked together.
If you want to ship a static library from Odin, compile .obj and if it has external C dependencies, ship with those .lib dependencies (foreign import
s should take care of linking). Also I don't think if you create an archive, that, foreign import
would be able to find them.
I'm not at home so I can't check whether Odin supports creating archives. If not, then maybe it's not even worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
core/build/build.odin
Outdated
return context | ||
} | ||
|
||
default_context :: proc() -> runtime.Context { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what's the roundabout with creating a global context with a logger, to then have the user retrieve that context. Can't default_context
create the context and return newly-created instead of returning a global context? I'm not seeing the purpose here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i can move the global into a @static
in default_context
, otherwise create_console_logger
would keep creating a new one on each call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…k for final API refactor
…Fixed argument parsing issue. Fixed relative target path issue
… automatic absolute path from build.odin in favor of setting them in user code.
… path to it. Finished up examples
… example is in place
I have finalized the API. Things might still need extra testing, but so far it seems to work. example build script: package src_build
import "core:build"
import "core:os"
import "core:strings"
import "core:fmt"
Mode :: enum {
Debug,
Release,
}
Target :: struct {
using target: build.Target,
mode: Mode,
}
project: build.Project
target_debug: Target = {
target = {
name = "dbg",
platform = {ODIN_OS, ODIN_ARCH},
},
mode = .Debug,
}
target_release: Target = {
target = {
name = "rel",
platform = {ODIN_OS, ODIN_ARCH},
},
mode = .Release,
}
run_target :: proc(target: ^build.Target, mode: build.Run_Mode, args: []build.Arg, loc := #caller_location) -> bool {
target := cast(^Target)target
config: build.Odin_Config
config.platform = target.platform
config.out_file = "demo.exe" if target.platform.os == .Windows else "demo"
// paths must be set with build.relpath / build.abspath in order for them to be relative to the build system root directory (build/../). build.t*path alternatives use context.temp_allocator
config.src_path = build.trelpath(target, "src")
config.out_dir = build.trelpath(target, fmt.tprintf("out/%s", target.name))
switch target.mode {
case .Debug:
config.opt = .None
config.flags += {.Debug}
case .Release:
config.opt = .Speed
}
switch mode {
case .Build:
// Pre-build stuff here
build.odin(target, .Build, config) or_return
// Post-build stuff here
case .Dev:
return build.generate_odin_devenv(target, config, args)
case .Help:
return false // mode not implemented
}
return true
}
@init
_ :: proc() {
project.name = "Build System Demo"
build.add_target(&project, &target_debug, run_target)
build.add_target(&project, &target_release, run_target)
}
main :: proc() {
info: build.Cli_Info
info.project = &project
info.default_target = &target_debug
build.run_cli(info, os.args)
} output of odin build "src"
-out:"out\dbg/demo.exe"
-subsystem:console
-build-mode:exe
-o:none
-reloc-mode:default
-debug
-target:windows_amd64 output of Syntax: build.exe <flags> <target>
Available Targets:
dbg
rel
Builtin Flags - Only 1 [Type] group per call. Groups are incompatible
-help
[Help] Displays information about the build system or the target specified.
-ols
[Dev] Generates an ols.json for the configuration.
-vscode
[Dev] Generates .vscode folder for debugging.
-build-pre-launch
[Dev] Runs the build system before debugging. (WIP)
-include-build-system:"<args>"
[Dev] Include the build system as a debugging target. (WIP)
-cwd:"<dir>"
[Dev] Sets the CWD to the specified directory.
-cwd-workspace
[Dev] Sets the CWD to the root of the build system executable.
-cwd-out
[Dev] Sets the CWD to the output directory specified in the -out odin flag
-launch-args:"<args>"
[Dev] The arguments to be sent to the output executable when debugging.
-dbg:"<debugger name>"
[Dev] Debugger type used. Works with -vscode. Sets the ./vscode/launch.json "type" argument output of {
"collections": [
],
"enable_document_symbols": true,
"enable_semantic_tokens": true,
"enable_hover": true,
"enable_snippets": true,
"checker_args": "-out:\"out/dbg/demo.exe\" -subsystem:console -build-mode:exe -o:none -reloc-mode:default -debug -target:windows_amd64"
}
{
"version": "0.2.0",
"configurations": [
{
"type": "cppvsdbg",
"request": "launch",
"preLaunchTask": "",
"name": "dbg",
"program": "C:\\dev\\Odin\\examples\\package_build\\out\\dbg\\demo.exe",
"args": [
""
],
"cwd": "${workspaceFolder}\\out\\dbg"
}
]
}
{
"version": "2.0.0",
"tasks": [
]
} Concept explanationThe idea of
In order to facilitate importing build systems into eachother, the Additional Features
Testing for yourselfOpen a command line in ODIN_ROOT/examples/package_build Final notesThe Future PRs (if this is accepted)
Future dream
|
@gingerBill have a look too when you got some free time. I'm having a weird time figuring out how to start other pull requests while this one is open since I pushed to main in the fork. If this one is merged, I'll start a new PR draft with documentation and all the other missing things, along with |
Try making a new branch and pushing that. $ git checkout -b other-pull-request
$ # Commit some stuff
$ git push -u my-remote other-pull-request I strongly recommend that you have two remotes, one for your fork and another for upstream and use the master branch to pull changes from upstream and merge onto pull-requests in-progress |
core/build/target.odin
Outdated
name: string, | ||
platform: Platform, | ||
|
||
run_proc: Target_Run_Proc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spaces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
exec :: proc(file: string, args: []string) -> int { | ||
//return _exec(file, args) // Note(Dragos): _exec is not properly implemented. Wait for os2 | ||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() | ||
cmd := strings.join(args, " ", context.temp_allocator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creates the issue when one of the arguments contains spaces. If one of the elements in the args
array contains a string with spaces, the user expects this string to be treated as a single argument (same as with execve()
call).
However after this concatenation it's gonna be multiple arguments e.g.
{"program", "a b c"} ---> {"program a b c"}
not
{"program", "a b c"} ---> {"program \"a b c\""}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'll add better support for this once os2
is in place. For now there might not be a point in getting this further than the main use case. I'll see if i can fix it quickly though
…cuting build.run_target inside another Target.target_proc
PR moved to #2958 |
This is an idea of how core:build API could look and function, for @gingerBill to review. This is still in drafting/experimenting stage, so there are some compiler flags that are not yet implemented (
-show-timings
for example)A (somewhat) minimal example was written here.
Example
Running the example
odin run build
(this is the simplest usage of the build system)out
, as configured by the build script.build
folder, while the actual app is insrc
Syntax
Running
odin run build -- -help
will give you an overview of all the available flags and configurations that you are able to call via the build system.Features
odin run build -- <config_name>
odin run build -- <config_name> -ols
. Theols.json
usually contains absolute paths, and I believe it's a good feature to be able to generate it and not have it as part of third party repositories as it is usually the case.odin run build -- <config_name> -vscode
(With some [WIP] flags that will be available in the future). Call the build system with the-vscode
flag, open vscode and hit F5. Different debuggers and editors will be added as requested.import
and use their configurations and setup dependencies.Known Issues
Since this is a draft and I don't know if it's gonna be accepted, the code has some memory leaks and the error handling is not ideal (relying on
eprintf
and not good error handling flow). The memory leaks are not a big issue in 99% of use cases, because core:build is meant to be used as a command line one-time utility.The dependency handling is still a big feature i want to play with, but i'm still experimenting. This feature is the main reason why the example API has an
add_project
and the setup is done in@init
, and i hope it will make more sense once that feature is in.