Created
February 26, 2021 02:20
-
-
Save sedme0/2e3e050319c6a02e7a6142ae0a5a817e to your computer and use it in GitHub Desktop.
Multi-language Hello World
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# ----------------------------------------------------------------------------- | |
# The purpose of this project is to create a Hello World program where each | |
# letter of "Hello world" is provided by a different programming language. This | |
# is achieved by writing a function in each language that returns the proper | |
# character. Each function is also to emit output of its own to stdout stating | |
# that it is responsible for its assigned letter. I have chosen to write the | |
# entire project in a shell script because I thought it would be interesting, | |
# which also happens to be the reason I chose to write the project at all. | |
# ----------------------------------------------------------------------------- | |
# Last modified February 25, 2021 | |
# ----------------------------------------------------------------------------- | |
PROJECTNAME=multiHelloWorld | |
# ----------------------------------------------------------------------------- | |
# C begins here | |
# ----------------------------------------------------------------------------- | |
cat << EOF | gcc -xc -c -o LetterH.o - | |
#include <stdio.h> | |
char getLetterH() { | |
printf("The letter H is brought to you by C.\n"); | |
return 'H'; | |
} | |
EOF | |
# ----------------------------------------------------------------------------- | |
# C ends here | |
# ----------------------------------------------------------------------------- | |
# ----------------------------------------------------------------------------- | |
# Rust starts here | |
# ----------------------------------------------------------------------------- | |
# Rust has to compile to a dynamic library, since as a static library, it creates linking errors. I should probably figure that out. | |
cat << EOF | rustc --crate-type cdylib -o libLetterE.so - | |
#[no_mangle] | |
pub extern "C" fn getLetterE() -> u8 { | |
println!("The letter E is brought to you by Rust."); | |
return b'e'; | |
} | |
EOF | |
# ----------------------------------------------------------------------------- | |
# Rust ends here | |
# ----------------------------------------------------------------------------- | |
# ----------------------------------------------------------------------------- | |
# Fortran starts here | |
# ----------------------------------------------------------------------------- | |
# -std-f2003 specifies Fortran 2003 because that's the version where the C bind thing was added | |
# -ffree-form specifies that it uses free form layout. Normally, this is inferred from file extension, but we're loading from stdin, so there is no file extension | |
# sed is used to give the function its capital letters back because Fortran is case-insensitive, and I want the function to match the style of the others | |
# I could use objcopy, and it would probably be more proper, but for such a small thing, it doesn't matter | |
cat << EOF | gfortran -xf95 -std=f2003 -fimplicit-none -S -ffree-form -o /dev/stdout - | sed 's/getletterl/getLetterL/g' | gcc -xassembler -c -o LetterL.o - | |
function getLetterL() bind(C) | |
character :: getLetterL | |
write (*, '(a)') 'The letter L is brought to you by Fortran.' | |
getLetterL = 'l' | |
end function getLetterL | |
EOF | |
# ----------------------------------------------------------------------------- | |
# Fortran ends here | |
# ----------------------------------------------------------------------------- | |
# ----------------------------------------------------------------------------- | |
# x86 Assembly starts here | |
# ----------------------------------------------------------------------------- | |
cat << EOF > LetterO.asm # nasm doesn't take stdin for some reason | |
global getLetterO | |
section .data | |
greeting db "The letter O is brought to you by x86 Assembly.", 0xa, 0 | |
len equ \$ - greeting | |
section .text | |
displayGreeting: ; This is a separate function so it looks nicer if you decompile it | |
mov edx, len ; greeting length | |
mov ecx, greeting ; greeting contents | |
mov ebx, 1 ; stdout | |
mov eax, 4 ; sys_write | |
int 0x80 ; call kernel | |
ret | |
getLetterO: | |
call displayGreeting | |
mov eax, 0x6F ; Returns the letter o | |
ret | |
EOF | |
nasm -f elf64 -o LetterO.o LetterO.asm | |
rm -f LetterO.asm | |
# ----------------------------------------------------------------------------- | |
# x86 Assembly ends here | |
# ----------------------------------------------------------------------------- | |
# ----------------------------------------------------------------------------- | |
# C# starts here | |
# ----------------------------------------------------------------------------- | |
# Microsoft's C# compiler is used instead of the Mono's because Mono's doesn't take stdin | |
# -nologo suppresses the copyright message on Microsoft's C# compiler. Probably doesn't do anything with the mono compiler. | |
cat << EOF | csc -nologo -target:library -out:LetterW.dll - | |
using System; | |
public class LetterW { | |
public static char getLetterW() { | |
Console.WriteLine("The letter W is brought to you by C#."); | |
return('w'); | |
} | |
} | |
EOF | |
# The following links contain useful information about this: https://www.mono-project.com/docs/advanced/embedding/ | |
# http://docs.go-mono.com/?link=xhtml:deploy/mono-api-embedding.html https://github.com/mono/mono/tree/master/samples/embed | |
# The Mono libraries are written in/for C, so that's the language I'm using for it. It's probably possible to use them with any language with C bindings. | |
# -w is used to suppress warnings. One of the warnings is caused by the mono libraries, another one is because this is sloppily written. | |
cat << EOF | gcc -xc -c $(pkg-config --cflags mono-2) -w -o LetterW.o - | |
#include <mono/jit/jit.h> | |
#include <mono/metadata/assembly.h> | |
#include <mono/metadata/debug-helpers.h> | |
char getLetterW() { | |
// Set up the environment | |
// Note: This could be set up as a macro for more complex projects | |
MonoDomain *domain = mono_jit_init("LetterWDomain"); | |
MonoAssembly *assembly = mono_domain_assembly_open(domain, "LetterW.dll"); | |
MonoImage* image = mono_assembly_get_image(assembly); | |
mono_config_parse("/etc/mono/config"); // If this isn't done, then C# system libraries won't work | |
// Set up the method | |
MonoMethodDesc* getLetterWDesc = mono_method_desc_new("LetterW:getLetterW()", NULL); | |
MonoMethod* getLetterWMethod = mono_method_desc_search_in_image(getLetterWDesc, image); | |
MonoObject *result = mono_runtime_invoke(getLetterWMethod, NULL, NULL, NULL); | |
return *(int *)mono_object_unbox(result); | |
} | |
EOF | |
# ----------------------------------------------------------------------------- | |
# C# ends here | |
# ----------------------------------------------------------------------------- | |
# ----------------------------------------------------------------------------- | |
# Python starts here | |
# ----------------------------------------------------------------------------- | |
# For more info see https://docs.python.org/3/c-api/ | |
cat << EOF | gcc -xc -c $(pkg-config --cflags python3-embed) -w -o LetterR.o - | |
#include <Python.h> | |
char getLetterR() { | |
Py_Initialize(); | |
PyObject *myModuleString = PyUnicode_FromString("__main__"); | |
PyObject *myModule = PyImport_Import(myModuleString); | |
PyRun_SimpleString("def getLetterR():\n print(\"The letter R is brought to you by Python.\")\n return('r')"); | |
PyObject *func = PyObject_GetAttrString(myModule, "getLetterR"); | |
PyObject *result = PyObject_CallObject(func, NULL); | |
char returnChar = *PyUnicode_AsUTF8(result); | |
Py_Finalize(); | |
return returnChar; | |
} | |
EOF | |
# ----------------------------------------------------------------------------- | |
# Python ends here | |
# ----------------------------------------------------------------------------- | |
# ----------------------------------------------------------------------------- | |
# Go starts here | |
# ----------------------------------------------------------------------------- | |
cat << EOF > LetterD.go # go build doesn't accept stdin for some reason | |
package main | |
import ( | |
"C" | |
"fmt" | |
) | |
//export getLetterD | |
func getLetterD() byte { | |
fmt.Println("The letter D is brought to you by Go.") | |
return byte('d') | |
} | |
func main() {} // There needs to be a main because golang says so arbitrarily | |
EOF | |
go build -buildmode=c-archive LetterD.go | |
mv LetterD.a libLetterD.a | |
rm -f LetterD.go | |
# ----------------------------------------------------------------------------- | |
# Go ends here | |
# ----------------------------------------------------------------------------- | |
# ----------------------------------------------------------------------------- | |
# C++ starts here | |
# ----------------------------------------------------------------------------- | |
# -xc++ specifies that it should compile c++, since there's no filename to infer from | |
cat << EOF | g++ -xc++ -c -Wno-narrowing -o ${PROJECTNAME}_main.o - | |
#include <iostream> | |
using std::cout; | |
using std::endl; | |
extern "C" char getLetterH(); // C | |
extern "C" char getLetterE(); // Rust | |
extern "C" char getLetterL(); // Fortran | |
extern "C" char getLetterO(); // Assembly | |
extern "C" char getLetterW(); // C# | |
extern "C" char getLetterR(); // Python | |
#include "LetterD.h" // Go | |
int main(void) { | |
char helloWorld[] = {getLetterH(), getLetterE(), getLetterL(), getLetterL(), getLetterO(), ' ', getLetterW(), getLetterO(), getLetterR(), getLetterL(), getLetterD(), '\0'}; | |
// cout << getLetterH() << getLetterE() << getLetterL() << getLetterL() << getLetterO() << ' ' << getLetterW() << getLetterO() << getLetterR() << getLetterL() << getLetterD() << endl; // This causes a segmentation fault | |
cout << helloWorld << endl; | |
} | |
EOF | |
rm -f LetterD.h | |
# ----------------------------------------------------------------------------- | |
# C++ ends here | |
# ----------------------------------------------------------------------------- | |
# -Wl,-rpath,. directs the dynamic linker to look in the same directory as the executable at runtime | |
# -no-pie is something that I read on the internet that I needed to fix the assembly stuff not linking, and it worked. | |
# -pthread is needed because Go uses threads | |
# -lgfortran ensures that the standard fortran libraries are linked | |
# -L. searches the current directory for libraries | |
g++ -o $PROJECTNAME -Wl,-rpath,. -no-pie -pthread ${PROJECTNAME}_main.o LetterH.o LetterL.o LetterO.o LetterW.o LetterR.o $(pkg-config --libs mono-2 python3-embed) -lgfortran -lLetterE -lLetterD -L. | |
rm -f LetterH.o LetterL.o LetterO.o LetterW.o LetterR.o libLetterD.a ${PROJECTNAME}_main.o # Clear temp files only needed for linking | |
./$PROJECTNAME | |
rm -f $PROJECTNAME libLetterE.so LetterW.dll # Clear temp files needed during execution |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment