Organizing School Workspace/Writing Makefile - linux

I am doing some C programming for school and I have found myself reusing libraries that I have created, over and over again (stacks, user input, error handling, etc).
Right now, my personal SVN directory structure looks like this:
trunk/
|-- 2520
| `-- assignments
| |-- A2
| |-- Makefile
| |-- README
| |-- calculator.c
| |-- calculatorlib.c
| `-- calculatorlib.h
`-- libs
|-- misc
| |-- errorlib.c
| |-- errorlib.h
| |-- userinputlib.c
| `-- userinputlib.h
`-- stacks
|-- stacklib.c
`-- stacklib.h
Some of these files (userinputlib and errorlib) get used in almost every project I work on for obvious reasons. I want to be able to include these files in a project's workspace (2520/assignments/A2) without having to copy the files because I don't want to manage to copies of a file and I don't want to check in two copies of the same file in SVN.
I would like to have the library files in the project workspace so my Makefile works without having to do to much manual configuration (or hard-coding paths).
At first, I thought about symbolic links (which SVN and tar support) but I can't seem to compile my assignment given my headers in another directory.
I can manually compile each header to an object file and do the final linking, but I'm not sure how to do this in a Makefile automatically.
Any help or any alternative to how I have my environment setup is appreciated.
Thanks!
EDIT: I forgot to mention that I have searched Google and have found a few pages describing automatic dependency generation (perhaps I want this?) using gcc -MM and I have read over the GNU Make manual but nothing jumped out at me.

Use could use subversion's externals feature to link a dynamic copy of the libs tree as a subdirectory of your project.
But in the end it may be better to just use a copy (a subversion copy, so it would effectively be a branch of the library code), so that you don't need to worry about changes to the library affecting existing projects, and can merge changes around as needed.

Why wouldn't you want to (svn) copy the necessary libs into your project? You'll essentially be creating a branch of your lib for use in your project. If you end up discovering a bug in your library code, you can fix it in place, and commit it back (to the copy). Once you're wrapping up, you can merge your fixes back to the library's trunk location.

Related

How to create an npm package that supports both local and global (-g) install with webpack and typescript?

I am trying to create an npm package that will have a local and a global (-g) installation option, but I am confused at the difference between the four directories (src vs lib and the purpose of bin).
Before, I would have used the src directory, and have webpack transpile and bundle (ts-loader + babel-loader) to the dist directory (with dist being hidden in gitignore). However, from what I looked at online, I should have instead bundle to lib directory, and manually create a bin directory that points to lib for executable cli (or global) packages?
Can someone tell me if my thought is correct? Should lib be added to gitignore, or should it be committed? What should I do about the bin directory? Is there a resource that you can point to for learning more about this?
I tried searching online for help with creating npm packages and searched through npmjs.com too, but I could not figure out what to do.
I also tried taking a quick look on github at what other projects did, but all I am able to derive so far is that the bin directory should include 1 index.js file that imports the main js file from the lib directory.
Thanks!
It doesn't really matter much when it comes to directory structure. Further, I would highly recommend that you use just the compiler Babel or TypeScript and generate required bundle (CJS or ESM). This way, you easily gets to preserve your source code directory structure.
If you cannot just use the compiler, then prefer Rollup over Webpack as it is excellent when it comes to bundling for libraries. The ESM output is still experiment with Webpack (as of version 5.x.x).
If you still decide to go ahead with Webpack, then this is what I would do. For a given directory structure:
<ROOT>
|-- src
| |-- app (actual library)
| | |-- index.js
| |-- cli (cli related code)
| | |-- index.js
And my content is:
// Library barrel file: src/app/index.js
export function main() {
console.log('Hello from library');
}
// CLI barrel file: src/cli/index.js
import { main } from '../app/index';
function cli() {
main();
}
cli();
// webpack.config.js
module.exports = {
mode: 'production',
entry: {
'cli/index': './src/bin/cli.js',
'library/index': './src/app/index.js'
},
output: {
filename: '[name].js',
library: {
type: 'commonjs'
}
},
externals: {
'../app/index': {
commonjs: '../app/index',
},
},
// USE ONLY IF LIBRARY TARGET IS MODULE
// experiments: {
// outputModule: true,
// }
// Rest of the configuration...
};
By default, it would create a dist folder with directory structure like this:
<ROOT>
|-- dist
| |-- cli
| | |-- index.js
| |-- library
| | |-- index.js
The, I can make use of package.json files bin and main field to respective compiles files:
{
"name": "my-lib",
"version": "1.0.0",
"main": "dist/library/index.js",
"types": "dist/library/index.d.js",
"bin": "dist/cli/index.js"
}
In above webpack configuration, note the use of externals which matches all the imports I have used. The default behavior of webpack is to always bundle and thus without this externals configuration, you would end up with library getting bundled twice for two entry points - one for cli and another as a library. (This is where Rollup helps greatly.) Further, you will have to do this for literally every third-party module that you imports or use webpack-node-externals
Few things to note:
Generally avoid having both (CLI & library) in the same package. Use something like my-library and my-libary-cli. The my-libary-cli package can specify my-library as a peerDependency.
As said earlier, what matters is the bin and main field. The folder structure doesn't matter.
Also, prefer just compiler over bundler. If you need to import custom assets like PNG, CSS, etc. then only bundler would be required. In that case, prefer rollup over webpack due to the clean output that Rollup generates. Use webpack for application level bundling.
To generate TS definition files, use tsc. Webpack won't do that for you.
The setup can get complicated easily if you intend to use new exports field instead of main field. So, I would prefer having a barrel pattern instead of allowing multiple sub imports. May things get wrong. TypeScript, Jest setup, etc. on consumer side.

Best way to share common logic between Typescript projects

Let's say I have multiple TS projects, and I want to share common logic between these projects in another TS library/project. Both projects are separately checked into source control.
Assume these packages exist:
project-1 - Common logic
project-2 - A project using project-1
I have seen/thought of multiple options:
Project 1 and 2 live in the same source directory. Links are made through TS project references (https://www.typescriptlang.org/docs/handbook/project-references.html#handbook-content).
main-project-dir/
|-- project-1/
| |-- src/
|-- project-2/
| |-- src/
Project 1 lives inside project 2 subdirectory using Git submodules (https://git-scm.com/book/en/v2/Git-Tools-Submodules). Links are then made through TS references.
|-- project-2/
| |-- lib/
| |-- project-1/
| |-- src/
Project 1 is installed as an npm package in project 2. This can be a git repository or an actual npm package as we all know (https://docs.npmjs.com/cli/v8/commands/npm-install#:~:text=a%20%3Cgit%20remote%20url%3E%20that%20resolves%20to%20(a))
project-2/
|-- node_modules/
| |-- project-1
|-- src/
| |-- index.ts
If there are any other options please tell me and share your thoughts.
Kind regards,
Indy Maat

How to read text file within library package

I’m trying to read some text files located within my lib crate. File structure looks like this:
workspace
|
|-- MyBin
| |-- src
| |-- main.rs
|
`-- MyLib
|-- src
|-- lib.rs
`-- text.txt
Alright so MyBin has MyLib as one of its dependencies. Within lib.rs I am using the std::fs::read_to_string function to get access to text.txt’s contents. But when I run cargo run on MyBin the relative path now starts within MyBin’s src folder rather than MyLib.
Any way to read text.txt even when calling read_to_string from another crate?
When using libraries you need to understand that your code is never actually run before it's linked to an executable that itself is being run.
read_to_string does not embed your files content at compile time in the lib / executable it only tells the program that it needs to go get the file at runtime.
The read_to_string takes either a relative or absolute path to the file. I assume in your case you used a relative path. Relative path are always interpreted at runtime relative to where the executable is being run from.
As the comment on your post suggested if you need the text.txt file to you when you run the program you either have to ship it with the executable or embed its content in the library (maybe as an array of string).

How to get SCons Install to behave the same way as build at different hierarchy levels?

My project is the following tree
|-- A
| `-- SConscript
|-- B
| `-- SConscript
`-- SConstruct
and I want to install A's content into /install/A, and B's into /install/B, I achieve this by two similar looking SConscripts called from the top SConstruct. SConstruct sets up env['INSTALL_DIR'] = '/install' and exports it.
The A SConscript looks like this:
Import('env')
env = env.Clone(
INSTALL_DIR = os.path.join(env['INSTALL_DIR'], "A"))
env.Alias('install', env['INSTALL_DIR'])
build_result_obj = Program(...)
env.Install(env['INSTALL_DIR'], build_result_obj)
and similar for B.
When both, A and B are outdated, and I am in A subdirectory, I can run scons -u there, and it will only build A. But if I run scons -u install there, then it would try to install B as well, causing it to build B too.
I could resolve it by having different Alias names for install (install-A, install-B) and a combined one for two, but I don't want to remember all such names. I just want the install to behave the same as build with respect to the current location. How to achieve that?
You'll have to add your install targets to the Default target list. There is a method env.Default() for this, please check the docs of SCons. Note, how you can add Aliases to the Default list, too (once defined they're pretty much treated like file targets).
Another thing to regard here is, that you shouldn't define the install Aliases as simply
Alias('name', path_to_folder)
Like in every other build system, SCons will regard your install folder as up to date, as soon as it exists...and then no updates of your to-be-installed files happen.
Instead, define the Alias after you called the Install builder, and add the return value...which represents the path to the "program" node:
build_result_obj = Program(...)
instobj = env.Install(env['INSTALL_DIR'], build_result_obj)
env.Alias('install', instobj)

Output the binaries in the project's root bin subfolder using cmake

I'm currently on a BF interpreter project. I decided to use CMake, and it works properly.
I settled for an out-of-source build, following the following tree :
+ Project root
|
+-- src/
+-- bin/
+-- build/ <- holds the "rubbish" generated by CMake when generating the Makefile
+-- CMakeLists.txt
When I want to build the project, I run, from the project's root folder :
cd build
cmake ..
make
In the CMakeLists.txt, I added the following line :
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
But, I've found it outputs the binaries in the build/bin folder, so I edited the line to :
SET(EXECUTABLE_OUTPUT_PATH "../bin/"
It works perfectly fine, but it is, IMHO, kind of ugly. Is there any "clean" way of doing this, that is without making assumptions about the project's structure, instead using something like set(EXECUTABLE_OUTPUT_PATH ${PROJECT_ROOT}/bin") ?
Thanks in advance for your replies and sorry for any English errors i may have made, as English isn't my first language.
You can set the variable CMAKE_RUNTIME_OUTPUT_DIRECTORY to achieve this - something like:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
Basically, the ${PROJECT_ROOT} variable you are looking for is PROJECT_SOURCE_DIR, CMAKE_SOURCE_DIR, or CMAKE_CURRENT_SOURCE_DIR. Each has a slightly different meaning, but for a basic project, these could well all point to the same directory.
Note that for the CMAKE_RUNTIME_OUTPUT_DIRECTORY variable to take effect, it must be set before the target is created, i.e. before the add_executable call in the CMakeLists.txt.
Also note that multi-configuration generators like MSVC will still append a per-configuration directory to this project root/bin folder.

Resources