So what if I wanted to write some C with NO Swift?
It’s been awhile since I wrote much C, and I certainly don’t have a solid VScode workflow for it.
I made a sample multi-file C project and I love that it’s 61.4% makefile and 38.6% C
https://github.com/carlynorama/Hello_C_using_VScode
References
- About VSCode & C/C++
- https://code.visualstudio.com/docs/cpp/config-clang-mac
- Visual Studio Code for C/C++ on Linux (2021) https://www.youtube.com/watch?v=9pjBseGfEPU
- https://stackoverflow.com/questions/52770066/debugging-c-using-visual-studio-code-on-windows
- https://code.visualstudio.com/docs/editor/variables-reference
- https://code.visualstudio.com/Docs/editor/tasks
- https://code.visualstudio.com/docs/editor/tasks-appendix
- https://code.visualstudio.com/docs/cpp/config-mingw
- About makefiles:
- https://bytes.usc.edu/cs104/wiki/makefile/
- https://cs.colby.edu/maxwell/courses/tutorials/maketutor/
make -p
to see all the pre defined vars and implicit rules that make makefiles magic (…and hard to follow)
Steps taken
- (VSCode, XCode command line tools already installed.)
- Installed Microsoft C/C++ extension
- made a folder, opened it
- added a
.gitignore
(see below) - made
hello_world.c
(see below) - hit the debug button and selected C tool (clang, e.g.)
- look at
tasks.json
file created
- look at
- Saw output in debug window! That was easy! Nice!
- try adding
second_file.c
andsecond_file.h
…. CLONK…
Undefined symbols for architecture arm64:
"_and_so_we_meet_again", referenced from:
_main in hello_world-5211a2.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
- manual compile check
clang hello_world.c second_file.c
./a.out
… worked fine. Do I need to add a make file?
-
Yes. I need to do one of two things
- A) in tasks.json change
"{$file}",
to"hello_world.c", "second_file.c",
which I did and it works just fine. - B) OR add a make file. (later)
- A) in tasks.json change
-
F5 to start the process of making a launch.json? Sure. See video.
-
make some small edits to launch.json, especially
"preLaunchTask": "C/C++: clang build active file"
— (Later)
-
create a test makefile (see
makefiles_for_testing/test
), runmake
andmake clean
to confirm make is installed and working. -
Get a real Makefile working
-
Add a task to tasks.json, add it launch.json
TODO: figure out adding CMake. https://code.visualstudio.com/docs/cpp/cmake-linux
Files
hello_world.c
#include <stdio.h>
#include "second_file.h"
int main(int argc, char *argv[]) {
printf("Hello World.");
and_so_we_meet_again();
}
second_file.c
#include <stdio.h>
#include "second_file.h"
void and_so_we_meet_again() {
printf("... and so we meet ...again?");
}
second_file.h
#pragma once
void and_so_we_meet_again();
final Makefile
CC=clang
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=hello_world.c second_file.c
OBJDIR=obj
_OBJECTS=$(SOURCES:.c=.o)
OBJECTS = $(patsubst %,$(OBJDIR)/%,$(_OBJECTS))
EXECUTABLE=hello_world
DEST_DIR=bin
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $(DEST_DIR)/$@
$(OBJDIR)/%.o: %.c
$(CC) $(CFLAGS) $< -o $@
# `make clean` removes contents of bin folder and any random .o files
clean:
rm -rf $(OBJDIR)/*.o
rm -rf $(DEST_DIR)/$(EXECUTABLE)
.gitignore
.DS_Store
#name of compiled program
hello_world
/*.dSYM
*.out
/bin
/obj
# in this example, go ahead and show the .vscode folder
#/.vscode
# C/C++ Files object files
*.o
*.a
*.so
VSCode Helper files
tasks.json
Created when I hit the debug button for the first time.
The first task, cppbuild
, is no longer being used by me, but I’ve left it in for the record.
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: clang build active file",
"command": "/usr/bin/clang",
"args": [
"-fcolor-diagnostics",
"-fansi-escape-codes",
"-g",
"hello_world.c",
"second_file.c",
"-o",
"${fileDirname}/bin/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": false
},
"detail": "Task generated by Debugger."
},
{
"type": "process", //should this be done as shell or process? right now no args
"label": "Build using make",
"command": "make",
"options": {
"cwd": "${workspaceFolder}"
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
],
"detail": "What does adding a detail do?"
},
],
"version": "2.0.0"
}
launch.json
This is the file that triggers building the makefile task via "preLaunchTask": "Build using make"
{
"version": "0.2.0",
"configurations": [
{
"name": "(lldb) Launch",
"type": "cppdbg",
"request": "launch",
//change this line to match tasks.json
"program": "${workspaceFolder}/bin/hello_world",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
//TBD on which I prefer.
//"externalConsole": true,
"externalConsole": false,
"MIMode": "lldb",
//added to compile before launch
//also needs to match tasks.json
"preLaunchTask": "Build using make",
}
]
}
Make Files for testing
hello make
all: one two three
one:
echo “create file one”
touch one
two:
echo “create file two”
touch two
three:
echo “create file three”
touch three
clean:
echo “clean up files”
rm -f one two three
super basic
# Always compiles everything.
#none of the calls are file names, so lets keep make from getting confused.
.PHONY: clean simple verbose
# `make` or `make simple` will compile the files into output file after -o
simple: hello_world.c second_file.c
clang hello_world.c second_file.c -o bin/hello_world
# `make verbose` will compile the files with -g (debugging information) and -Wall (compiler warnings)
verbose: hello_world.c second_file.c
clang -g -Wall hello_world.c second_file.c -o bin/hello_world
# `make clean` removes contents of bin folder, there isn't a "clean"
clean:
rm -rf bin/*
explicit two file set up
all: hello_world
# compiles main program
hello_world: hello_world.c second_file.o
clang -g -Wall hello_world.c bin/second_file.o -o bin/hello_world
# will compile second file if needed
second_file.o: second_file.c second_file.h
clang -g -Wall -c second_file.c -o bin/second_file.o
# `make clean` removes contents of bin folder
# run even if there is a file called clean
.PHONY: clean
clean:
rm -rf bin/*
Start adding variables
CC=clang
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=hello_world.c second_file.c
OBJECTS=$(SOURCES:.c=.o)
EXECUTABLE=hello
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
.c.o:
$(CC) $(CFLAGS) $< -o $@
# `make clean` removes .o files and compiled program
# run even if there is a file called clean
.PHONY: clean
clean:
rm -f *.o $(EXECUTABLE)
Put built files in sub folders
CC=clang
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=hello_world.c second_file.c
OBJDIR=obj
_OBJECTS=$(SOURCES:.c=.o)
OBJECTS = $(patsubst %,$(OBJDIR)/%,$(_OBJECTS))
EXECUTABLE=hello
DEST_DIR=bin
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $(DEST_DIR)/$@
$(OBJDIR)/%.o: %.c
$(CC) $(CFLAGS) $< -o $@
# `make clean` removes contents of bin folder and any random .o files
# run even if there is a file called clean
.PHONY: clean
clean:
rm -rf *.o
rm -rf $(DEST_DIR)/*