|
|

Symbolic Linking

Symlinking by Example: Simplifying Clangd Configuration

This short post is meant to serve as a brief overview of symbolic linking by using Clangd user configuration files as an example of the simple but powerful utility symlinking with the ln command offers; This post is about symbolic linking, not Clangd but a little context is needed to make this example meaningful.

Clangd & Configuration Types:

Clangd is a language server protocol implementation that provides essential support for C and C++ projects, such as code completion, diagnostics, and formatting. From the clangd configuration page you can find there are two types of configurations that can be used:

Project configurations are set by placing a .clangd file in the project root directory, and take precedence over user configurations. These are ideal for shared projects or as projects scale, ensuring code style consistency and proper project configuration for all developers.

User configurations allow you to specify a default Clangd config for projects that don’t have a .clangd file. On MacOS, the default Clangd user configuration file location is ~/Library/Preferences/clangd/config.yaml. However, this default location goes against the convention of placing configuration files in the ~/.config directory (as is the case for clangd on Linux systems).

Symlinking with ln:

To align with the standard convention and still have Clangd recognize the user configuration, we can create a sub-directory and file in ~/.config/, then symlink it to the prescribed directory using the ln command:

1
2
3
mkdir ~/.config/clangd
touch ~/.config/clangd/config.yaml
ln -s ~/.config/clangd ~/Library/Preferences

Now, if we run ls -a in ~/Library/Preferences, we can see that the /clangd directory now exists in the required location, with the -> symbol indicating the “real” location of the symlink, i.e. ~/.config/clangd

1
2
andrew:~/Library/Preferences $ ls -a | grep clang
lrwxr-xr-x@  1 andrew  staff    28B 22 Dec 02:33 clangd -> /Users/andrew/.config/clangd

If we cd into ~/Library/Preferences/clangd and ls the contents of the directory, we can see that config.yaml is also contained in the directory and verifies that ~/Library/Preferences/clangd mirrors ~/.config/clangd

With this setup, we can modify config.yaml from the ~/.config/clangd directory, and the symlinked directory and file will mirror this “real” copy.

The Power of Symlinking:

Symlinks are a powerful and versatile tool that offer numerous benefits in various contexts. Some of the key advantages of using symlinking include:

Organizing files and directories: By creating shortcuts to frequently used or deeply nested locations, symlinks simplify navigation and file management in complex directory structures.

Sharing libraries and resources: Symlinks enable the sharing of libraries, configuration files, or other resources among different projects or applications without duplicating them. This saves storage space and ensures that updates to shared resources are automatically propagated.

Version management: By linking to different versions of software, libraries, or other resources, symlinks make it easy to switch between versions without modifying the configurations of dependent applications.

Backup and synchronization: Creating references to files or directories in backup or synchronization systems using symlinks ensures that changes made to the original files are automatically mirrored in the backup location.

These examples highlight the versatility of symlinking in various situations, from managing files and directories to sharing resources. Symlinking is a fundamental tool for operating system management, and is often used heavily by package managers to provide the magic-like functionality they offer. Having solid understanding of symbolic linking and how it works can make tasks more efficient and streamlined, after getting comfortable with it’s usage, you’ll start to recognize dozens of ways to creatively wield such a powerful tool.


There’s a few other important things to know about linking.
Lets look at an example file structure where there are currently no links of any kind:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
andrew:~/.tmp/symlink-example $ tree
.
├── Here
│   ├── here.txt
│   └── here2.txt
└── There
    ├── there.txt
    └── there2.txt

3 directories, 4 files

Lets hard-link /Here/here.txt to /There/
and soft-link (symbolic-link) /Here/here2.txt to /There/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
andrew:~/.tmp/symlink-example $ ln Here/here.txt There && ln -s Here/here2.txt There
andrew:~/.tmp/symlink-example $ tree
.
├── Here
│   ├── here.txt
│   └── here2.txt
└── There
    ├── here.txt
    ├── here2.txt -> Here/here2.txt
    ├── there.txt
    └── there2.txt

3 directories, 6 files

Notice that /There/here2.txt has an -> pointing to the location of the file it is mirroring, whereas There/here.txt does not.

We can investigate further by looking at the inodes of the files in the respective directories:

1
2
3
4
5
6
7
andrew:~/.tmp/symlink-example $ ls -li Here && ls -li There

151735029 here.txt  // original
151735195 here2.txt // original

151735029 here.txt                    // hard-linked
151735472 here2.txt -> Here/here2.txt // soft-linked

Notice that the hard-link and it’s respective original have the same inode value, whereas the soft-link and it’s respective original have different inode values.

Lets look at some interesting properties of hard vs soft linked files:

Size

We use the -sh options with tree command to print out the file size in bytes along side the names:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ tree -sh
[ 160]  .
├── [ 128]  Here
│   ├── [  10]  here.txt
│   └── [  10]  here2.txt
└── [ 224]  There
    ├── [  10]  here.txt
    ├── [  14]  here2.txt -> Here/here2.txt
    ├── [  10]  there.txt
    └── [  12]  there2.txt

Notice that the hard-linked file (/There/here.txt) and it’s original have the same file size, whereas the soft-linked file is larger than it’s respective original. This is a bit misleading at first glance because the files are empty.

To explain why the soft-linked file seems larger then it’s original and to demostrate the difference between hard and soft linking I’m going to add a longer snippet of text to both the original txt files and print out the file size again.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ tree -sh
[ 160]  .
├── [ 128]  Here
│   ├── [ 562]  here.txt
│   └── [ 562]  here2.txt
└── [ 224]  There
    ├── [ 562]  here.txt
    ├── [  14]  here2.txt -> Here/here2.txt
    ├── [  10]  there.txt
    └── [  12]  there2.txt

We can see that the hard-linked file There/here.txt reports the same size as it’s original, whereas the soft-linked file There/here2.txt reports a size of 14B.

This is because the “contents” of the soft-linked file is simply a file path to the original, which is this case is 14B, thus adding any amount of text the original will not have any effect the size of the soft-linked file.

Relating this back to inode values; hard-links share the same inode, meaning that they are actually the same file sharing the same storage address. This provides a way of having the exact same file in multiple locations without taking up any additional storage space, as would be the case if we copy+pasted the file, with the additional benefit of ensuring that the two files are always the exact same and having the ability to acess the file from multiple locations.

However; Soft-linked files behave differently. Soft-links (or symbolic links) have a unique inode from their respective original. This indicates that a soft-linked file and it’s respective original don’t share the same storage address and are seperate and unique files. This is depicted below:

/Here/herAeSd.tdtorxretasgseX/There/here.txtA/SdHtdeorrreeas/gsheeYre2.txtASdtd/orTrehasegsreeZ/here2.txt

As you can see, modifying either the hard-linked file or it’s original is the same as modifying the data stored in Address X.

In contrast to the soft-linked file and it’s original, only Address Y contains data and Address Z just contains a reference to a file location which itself stores data in Address Y. This is made clear if you try to print or modify the contents of /There/here2.txt and get a “There/here2.txt: No such file or directory” message.


TBC