More CS50. I decided I wanted to be able to work on the assignments in C on my development system. This was before setting up the Flask environment), which was painful enough in itself. Likely due to a significant lack of knowledge on my part.

I had made a start on getting VSCode to work with C. But never really got it working the way I wanted. So, I am going to see if I can actually get it working in a more or less proper fashion. That is, having makefiles work. Not just compiling and running a single C code file — which is as far as I got previously.

Also, I would need to somehow compile and link the CS50 library (cs50.h and cs50.c).

This would sure be easier on Linux, and perhaps a Mac.

Don’t really know if I can get it to work as my research so far has been less than englightening. But no journey has ever been completed without at least heading out the door. Should be challenging and fun.

Initial Attempt

My initial thought was to install GCC using mingw-w64. That was pretty much what most of the articles I had looked at recommended or suggested. Well, after a few failed attempts (could never get it to complete the installation) I gave up on that idea. And, instead installed tdm-gcc. I used the 64+32-bit MinGW-w64 edition. It is, of course, installed on my dev partition, E:\appDev\TDM-GCC-64. As usual in my case, it is not in my Path environment variable. (And, until I wrote the previous line, I didn’t think to try using conda to create a C virtual environment. Go figure! Perhaps later. And, I had decided not to mess with WSL for the time being as well.)

PS R:\learn\edX\cs50> E:\appDev\TDM-GCC-64\bin\gcc --version
gcc.exe (tdm64-1) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ok, time for ‘Hello, world!’.

// \learn\edX\cs50\rek_hello.c
#include <stdio.h>

int main(void)
{
    printf("Hello, world!\n");
    return 0;
}
PS R:\learn\edX\cs50> E:\appDev\TDM-GCC-64\bin\gcc -o rek_hello rek_hello.c
PS R:\learn\edX\cs50> dir

    Directory: R:\learn\edX\cs50


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2021-03-15     15:32                .vscode
-a----        2021-03-15     16:16            312 .gitignore
-a----        2021-03-15     14:48            250 cs50.code-workspace
-a----        2021-03-23     10:16            108 rek_hello.c
-a----        2021-03-23     10:26         330355 rek_hello.exe
-a----        2021-03-22     11:29           8533 rek_notes.txt

PS R:\learn\edX\cs50> ./rek_hello
Hello, world!

PS R:\learn\edX\cs50>

Okay, the easy stuff is done.

Makefile

I previously never tried to create and use a makefile. But, I’ve decided I may as well give it a shot right now.

all: rek_hello.exe

rek_hello.exe: rek_hello.o
	E:\appDev\TDM-GCC-64\bin\gcc rek_hello.o -o rek_hello.exe

rek_hello.o: rek_hello.c
	E:\appDev\TDM-GCC-64\bin\gcc -c rek_hello.c

clean:
	rm rek_hello.o rek_hello.exe
PS R:\learn\edX\cs50> E:\appDev\TDM-GCC-64\bin\mingw32-make
makefile:4: *** missing separator (did you mean TAB instead of 8 spaces?).  Stop.

Damn, my VSCode configuration converts tabs to spaces (2 of them, don’t start…). So, down in the bottom bar I clicked on Spaces: 2, and selected ‘Indent Using Tabs`. Also fixed a few cases where I had ‘hello’ instead of ‘rek_hello’.

PS R:\learn\edX\cs50> E:\appDev\TDM-GCC-64\bin\mingw32-make
E:\appDev\TDM-GCC-64\bin\gcc rek_hello.o -o rek_hello.exe
PS R:\learn\edX\cs50> .\rek_hello
Hello, world!

That is progress. (Enough for the moment, have some gardening to do.)

CS50

I had planned to tackle getting things working in VSCode next. But, I’ve decided to go all out and see if I can compile and link the CS50 library with a simple hello, ??? program. I want to use the get_string() function provided by that library. Expect there will be problems along the way. Including writing a suitable makefile. Let’s start by downloading the CS50 files. I didn’t use git, I downloaded the zip (sorry).

My directory structure looks something like this (the barebones):

r:/learn/edX
│
├── cs50/
│   │
│   ├── include/
│   │   └── cs50.h
│   │
│   │─── lib/
│   │    └── cs50.c
│   │
│   │─── rek_1/
│   │    ├── Makefile
│   │    └── rek_hello.c
│   │
│   ├── .gitignore
│   ├── cs50.code-workspace
│   ├── rek_notes.txt
│   └── rek_recent_web_pages.txt
...

I rewrote rek_hello.c to use the CS50 C library.

// \learn\edX\cs50\rek_1\rek_hello.c
#include <cs50.h>
#include <stdio.h>

// string is in cs50.h and get_string in cs50.c
int main(void)
{
    string name = get_string("Enter your name: ");
    printf("Hello, %s!\n", name);
}

So, now I need to produce object code for both cs50.c and rek_hello.c. Ensuring the compiler knows where to find cs50.h. Then link them together into the executable which I am going to call hello_you.exe.

Makefile

Okay, this is definitely going to require a slightly more complex Makefile. I will use some variables, some functions, automatic variables, and the like. Sorting that took some research and time. Even though it is going to look rather simple. I also, in the early going, made a small but important mistake.

I decided to start by getting the original hello, world working with a fancier Makefile. Something like the following. I have moved this and some other test files to a new directory play_stuff. And, I have added a subdirectory called obj. And, don’t forget that tab at the start of each command under a rule.

Hopefully the comments will explain most of this.

# Specify compiler
CC = E:\appDev\TDM-GCC-64\bin\gcc

# final application name
LINK_TARGET = hello_world.exe

# compiler flags
CFLAGS = -Wall -Wextra -Werror -pedantic -std=c11

# define all the object files, adding there location, .\obj\
# designed to all me to define multiple files in _OBJ
# object dir
ODIR = obj
# list of required object files
_OBJ = rek_hello.o
# add path to each object file
OBJS = $(patsubst %,$(ODIR)\\%,$(_OBJ))

# default target to run
all : $(LINK_TARGET)

# create the required object files
# $@ - the file name of the target of the rule
#      i.e. obj\rek_hello.o
# $^ - all the prerequisites, with spaces between them
#      i.e. rek_hello.c
# $(X) - replace with the variable X's value
$(ODIR)\rek_hello.o : rek_hello.c
	# print some debug stuff
	@echo $(OBJS)
	@echo $@ $^
	$(CC) -c $^ $(CFLAGS) -o $@

# create final executable, linking the oject files 
$(LINK_TARGET) : $(OBJS)
	$(CC) $^ -o $@ $(CFLAGS)

# in makefile on linux or such, would use 'rm' or somesuch, but don't work in Windows
# when called, e.g. 'makefile clean', delete all the object files and the executable
clean:
	del obj\*.o
	del $(LINK_TARGET)

And…

PS R:\learn\edX\cs50\play_stuff> E:\appDev\TDM-GCC-64\bin\mingw32-make
obj\rek_hello.o rek_hello.c
E:\appDev\TDM-GCC-64\bin\gcc -c rek_hello.c -Wall -Wextra -Werror -pedantic -std=c11 -o obj\rek_hello.o
E:\appDev\TDM-GCC-64\bin\gcc obj\rek_hello.o -o hello_world.exe -Wall -Wextra -Werror -pedantic -std=c11

PS R:\learn\edX\cs50\play_stuff> ls -recurse

    Directory: R:\learn\edX\cs50\play_stuff

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2021-03-24     15:19                obj
-a----        2021-03-24     15:19         330355 hello_world.exe
-a----        2021-03-24     15:14           1143 Makefile
-a----        2021-03-23     10:16            108 rek_hello.c

    Directory: R:\learn\edX\cs50\play_stuff\obj

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021-03-24     15:19            866 rek_hello.o

PS R:\learn\edX\cs50\play_stuff> .\hello_world
Hello, world!

Okay, let’s move on to the Makefile for hello_you.exe.

# Specify compiler
CC = E:\appDev\TDM-GCC-64\bin\gcc
# Specify linker
LINK = E:\appDev\TDM-GCC-64\bin\gcc

LINK_TARGET = hello_you.exe

# include dir
IDIR = ..\include
# lib dir
LDIR = ..\lib

# flags for include and library locations
IFLG = -I $(IDIR)
LFLG = -L $(LDIR)

# let's define our header file dependencies
_DEPS = cs50.h
DEPS = $(patsubst %,$(IDIR)\\%,$(_DEPS))

# and the exact path and name of the cs50 code file
_LIBS = cs50.c
LIBS = $(patsubst %,$(LDIR)\\%,$(_LIBS))

# create a list, with path, of the object files we will be needing
ODIR = obj
_OBJ = rek_hello.o cs50.o
OBJS = $(patsubst %,$(ODIR)\\%,$(_OBJ))

# Build all target
.PHONY : all

all : app

# compile the object files, two in this case
$(ODIR)\rek_hello.o : rek_hello.c $(DEPS)
# print some debug stuff
	@echo $(DEPS)
	@echo $(LIBS)
	@echo $(OBJS)
	@echo $@ $<
	$(CC) -c -o $@ $< $(IFLG)

$(ODIR)\cs50.o : $(LIBS) $(DEPS)
	@echo $@ $<
	$(CC) -c -o $@ $< $(IFLG)

# build the executable
app : $(OBJS)
	$(CC) -o $(LINK_TARGET) $^ $(CFLAGS) $(DEPS) $(IFLG)

.PHONY : clean

# get rid fo the object files and executables for clean start
clean:
	del $(ODIR)\*.o
	del $(LINK_TARGET)
PS R:\learn\edX\cs50\rek_1> E:\appDev\TDM-GCC-64\bin\mingw32-make
..\include\cs50.h
..\lib\cs50.c
obj\rek_hello.o obj\cs50.o
obj\rek_hello.o rek_hello.c
E:\appDev\TDM-GCC-64\bin\gcc -c -o obj\rek_hello.o rek_hello.c -I ..\include
obj\cs50.o ..\lib\cs50.c
E:\appDev\TDM-GCC-64\bin\gcc -c -o obj\cs50.o ..\lib\cs50.c -I ..\include
E:\appDev\TDM-GCC-64\bin\gcc -o hello_you.exe obj\rek_hello.o obj\cs50.o  ..\include\cs50.h -I ..\include

When I was working on the above, I initially left out/accidentally dropped the lines:

# Build all target
.PHONY : all

all : app

Give it a try and see what happens. Drove me nuts for a bit of time.

And, does it work?

PS R:\learn\edX\cs50\rek_1> .\hello_you
Enter your name: koach
Hello, koach!

Half-way There

I think that’s it for this one. I had thought I’d get to working on the set-up for VS Code. But, this one is plenty long enough and has taken me plently long enough. And, there’s still lots of garden work to be done. Not to mention continue working on CS50. And, I also signed up for Josh Comeau’s CSS for JavaScript Developers. So also plenty on the plate in general.

Until next time. Hopefully I will have figured out how to configure VS Code to compile/run C projects. Will continue with the hello, you! application.

P.S. In an e-mail exchange, I learned that a nephew had installed/setup WSL on his Windows PC. Has more or less convinced me to give it a try. Likely in the next week or two.

Resources