rsync --copy-unsafe-links problem with a relative symlink (does not copy) - copy

When I use rsync with option --copy-unsafe-links it does not work when a symlink is relative. If a simlink is absolute it works as expected: copies content of the target instead of creating a broken symlink.
I have a dir structure in /tmp:
rsync-problem
├── dst
└── src
├── a
│ ├── a-file
│ ├── b-symlink -> ../b
│ └── c-symlink -> /tmp/rsync-problem/src/c
├── b
│ └── b-file
└── c
└── c-file
after running rsync:
rsync \
--copy-unsafe-links \
--links \
--recursive \
/tmp/rsync-problem/src/a \
/tmp/rsync-problem/dst
I get a broken relative symlink: b-symlink -> ../b in the rsync-problem/dst/a dir and a properly converted absolute symlink into a file rsync-problem/dst/a/c-symlink/c-file
rsync-problem
├── dst
│ └── a
│ ├── a-file
│ ├── b-symlink -> ../b
│ └── c-symlink
│ └── c-file
└── src
├── a
│ ├── a-file
│ ├── b-symlink -> ../b
│ └── c-symlink -> /tmp/rsync-problem/src/c
├── b
│ └── b-file
└── c
└── c-file
man of rsync for --copy-unsafe-links:
This tells rsync to copy the referent of symbolic links that point outside the copied tree. Absolute symlinks are also
treated like ordinary files, and so are any symlinks in the source
path itself when --relative is used. This option has no additional
effect if --copy-links was also specified.
Does not state that this option has no effect when a relative symlink is encountered.
and if I understand correctly
This tells rsync to copy the referent of symbolic links that point
outside the copied tree.
/tmp/rsync-problem/src/b
is outside the copied src tree:
/tmp/rsync-problem/src/a
what am I doing wrong and how to make rsync convert relative symlinks into files as it does with the absolute ones?
Script to replicate the issue:
#!/bin/sh
set -ue
mkdir -p /tmp/rsync-problem/dst
mkdir -p /tmp/rsync-problem/src/a
touch /tmp/rsync-problem/src/a/a-file
mkdir -p /tmp/rsync-problem/src/b
touch /tmp/rsync-problem/src/b/b-file
mkdir -p /tmp/rsync-problem/src/c
touch /tmp/rsync-problem/src/c/c-file
cd /tmp/rsync-problem/src/a
ln -sr ../b b-symlink
ln -s /tmp/rsync-problem/src/c /tmp/rsync-problem/src/a/c-symlink
rsync \
--copy-unsafe-links \
--links \
--recursive \
/tmp/rsync-problem/src/a \
/tmp/rsync-problem/dst

Related

Console scripts outside the main module aren't packaged along (ModuleNotFoundError)

Behold, my tree:
src/
├── bin
│ ├── cli.py
├── mypackage
│ ├── __init__.py
│ ├── api.py
│ ├── models
│ └── utils.py
Now, observe, my setup.cfg:
[options]
zip_safe = False
packages = find:
package_dir =
=src
[options.package_data]
src/bin = *
[options.packages.find]
where =
src
[options.entry_points]
console_scripts =
mycommand = bin.cli:main
That seemed fairly reasonable to me. Yet when I install this package in a new virtual environment (pip install /path/to/package), and I run mycommand, I get a very unreasonable answer: ModuleNotFoundError: No module named 'bin'.
NOTE this does work if I do an editable install!
What am I doing wrong?

Recipe for MV BlueFox3 using Yocto and a minimal image

I am using MV BlueFox3 camera on a RaspberryPi4 and I am trying to write a .bb recipe which is going to install the necessary software for the camera. A quick setup guide is on the manufacturer website, but I can not get the first part to work. So far, this is how my recipe looks:
SUMMARY = "Installs mvIMPACT Acquire base libraries"
DESCRIPTION = "This recipe installs mvIMPACT Acquire base libraries for all MATRIX VISION devices."
HOMEPAGE = "http://www.matrix-vision.de"
LICENSE_FLAGS = "EULA"
LICENSE_FLAGS_WHITELIST = "EULA"
LICENSE = "EULA"
LIC_FILES_CHKSUM = "file://${WORKDIR}/mvIMPACT_Acquire-ARM64-2.45.0/doc/EULA.txt;md5=f6f99507036166a5604b38e6df10f004"
PACKAGE_ARCH = "${MACHINE_ARCH}"
S = "${WORKDIR}"
SRC_URI = "file://mvGenTL_Acquire-ARM64_gnu-2.45.0.tgz"
TARGET = "arm64"
MVIA_SUBDIR = "opt/mvIMPACT_Acquire"
MVIA_LIB_SUBDIR = "lib"
TOOLKITS_LIB_SUBDIR = "Toolkits"
MV_DATA_DIR = "opt/mvIMPACT_Acquire/data"
do_install() {
# install mvIMPACT Acquire runtime binaries
install -m 0755 -d ${D}${base_prefix}/${MVIA_SUBDIR}/${MVIA_LIB_SUBDIR}
install -m 0755 -d ${D}${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}
install -m 0755 -d ${D}${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/FreeImage3160/bin/Release/FreeImage/${TARGET}
oe_soinstall ${S}/mvIMPACT_Acquire-ARM64-${PV}/lib/${TARGET}/libmvDeviceManager.so.${PV} ${D}${base_prefix}/${MVIA_SUBDIR}/${MVIA_LIB_SUBDIR}
oe_soinstall ${S}/mvIMPACT_Acquire-ARM64-${PV}/lib/${TARGET}/libmvPropHandling.so.${PV} ${D}${base_prefix}/${MVIA_SUBDIR}/${MVIA_LIB_SUBDIR}
oe_soinstall ${S}/mvIMPACT_Acquire-ARM64-${PV}/Toolkits/expat/bin/${TARGET}/lib/*.so.* ${D}${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}
install -m 0755 ${S}/mvIMPACT_Acquire-ARM64-${PV}/Toolkits/FreeImage3160/bin/Release/FreeImage/${TARGET}/*.so ${D}${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/FreeImage3160/bin/Release/FreeImage/${TARGET}
# set environment variables for mvIMPACT Acquire
install -m 0755 -d ${D}${sysconfdir}/profile.d
echo 'export MVIMPACT_ACQUIRE_DIR=/opt/mvIMPACT_Acquire' >> ${D}${sysconfdir}/profile.d/acquire.sh
echo 'export MVIMPACT_ACQUIRE_DATA_DIR=/opt/mvIMPACT_Acquire/data' >> ${D}${sysconfdir}/profile.d/acquire.sh
# set library search paths for the dynamic linker
install -m 0755 -d ${D}${sysconfdir}/ld.so.conf.d
echo '/opt/mvIMPACT_Acquire/lib' >> ${D}${sysconfdir}/ld.so.conf.d/acquire.conf
echo '/opt/mvIMPACT_Acquire/Toolkits' >> ${D}${sysconfdir}/ld.so.conf.d/acquire.conf
# set up logfiles
install -m 0755 -d ${D}${base_prefix}/${MV_DATA_DIR}/logs
install -m 0755 ${S}/mvIMPACT_Acquire-ARM64-${PV}/apps/mvDebugFlags.mvd ${D}${base_prefix}/${MV_DATA_DIR}/logs/
}
INHIBIT_PACKAGE_STRIP = "1"
INSANE_SKIP_${PN} += "dev-so \
already-stripped \
ldflags"
PACKAGES = "${PN} ${PN}-dev ${PN}-doc"
FILES_${PN} += "${base_prefix}/${MVIA_SUBDIR}/${MVIA_LIB_SUBDIR}/lib*.so.* \
${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/lib*.so.* \
${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/FreeImage3160/bin/Release/FreeImage/${TARGET}/lib*.so \
${sysconfdir}/profile.d/*.sh \
${sysconfdir}/ld.so.conf.d/*.conf"
FILES_${PN}-dev += "${base_prefix}/${MVIA_SUBDIR}/${MVIA_LIB_SUBDIR}/lib*.so \
${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/lib*.so"
FILES_${PN}-doc += "${base_prefix}/${MV_DATA_DIR}/logs"
but I am getting the following error:
NOTE: Executing Tasks
ERROR: mvimpact-acquire-base-2.45.0-r0 do_package: QA Issue: File '/opt/mvIMPACT_Acquire/Toolkits/FreeImage3160/bin/Release/FreeImage/arm64/libfreeimage-3.16.0.so' from mvimpact-acquire-base was already stripped, this will prevent future debugging! [already-stripped]
ERROR: mvimpact-acquire-base-2.45.0-r0 do_package: QA Issue: File '/opt/mvIMPACT_Acquire/Toolkits/libexpat.so.1.6.0' from mvimpact-acquire-base was already stripped, this will prevent future debugging! [already-stripped]
ERROR: mvimpact-acquire-base-2.45.0-r0 do_package: QA Issue: File '/opt/mvIMPACT_Acquire/lib/libmvDeviceManager.so.2.45.0' from mvimpact-acquire-base was already stripped, this will prevent future debugging! [already-stripped]
ERROR: mvimpact-acquire-base-2.45.0-r0 do_package: QA Issue: File '/opt/mvIMPACT_Acquire/lib/libmvPropHandling.so.2.45.0' from mvimpact-acquire-base was already stripped, this will prevent future debugging! [already-stripped]
ERROR: mvimpact-acquire-base-2.45.0-r0 do_package: QA Issue: mvimpact-acquire-base: Files/directories were installed but not shipped in any package:
/opt
/opt/mvIMPACT_Acquire
/opt/mvIMPACT_Acquire/Toolkits
/opt/mvIMPACT_Acquire/lib
/opt/mvIMPACT_Acquire/data
/opt/mvIMPACT_Acquire/Toolkits/libexpat.so
/opt/mvIMPACT_Acquire/Toolkits/libexpat.so.1.6.0
/opt/mvIMPACT_Acquire/Toolkits/libexpat.so.1
/opt/mvIMPACT_Acquire/Toolkits/FreeImage3160
/opt/mvIMPACT_Acquire/Toolkits/FreeImage3160/bin
/opt/mvIMPACT_Acquire/Toolkits/FreeImage3160/bin/Release
/opt/mvIMPACT_Acquire/Toolkits/FreeImage3160/bin/Release/FreeImage
/opt/mvIMPACT_Acquire/Toolkits/FreeImage3160/bin/Release/FreeImage/arm64
/opt/mvIMPACT_Acquire/Toolkits/FreeImage3160/bin/Release/FreeImage/arm64/libfreeimage-3.16.0.so
/opt/mvIMPACT_Acquire/lib/libmvPropHandling.so
/opt/mvIMPACT_Acquire/lib/libmvDeviceManager.so.2.45.0
/opt/mvIMPACT_Acquire/lib/libmvDeviceManager.so.2
/opt/mvIMPACT_Acquire/lib/libmvDeviceManager.so
/opt/mvIMPACT_Acquire/lib/libmvPropHandling.so.2.45.0
/opt/mvIMPACT_Acquire/lib/libmvPropHandling.so.2
/opt/mvIMPACT_Acquire/data/logs
/opt/mvIMPACT_Acquire/data/logs/mvDebugFlags.mvd
Please set FILES such that these items are packaged. Alternatively if they are unneeded, avoid installing them or delete them within do_install.
mvimpact-acquire-base: 22 installed and not shipped files. [installed-vs-shipped]
ERROR: mvimpact-acquire-base-2.45.0-r0 do_package: Fatal QA errors found, failing task.
ERROR: Logfile of failure stored in: /home/stefan/Projects/raspberrypiOS/build/tmp/work/raspberrypi4_64-poky-linux/mvimpact-acquire-base/2.45.0-r0/temp/log.do_package.2280592
ERROR: Task (/home/stefan/Projects/raspberrypiOS/meta-mvimpact-acquire/recipes-mvimpactacquire/mvimpact-acquire/mvimpact-acquire-base_2.45.0.bb:do_package) failed with exit code '1'
I am guessing that I should change the FILES variable to ensure proper package splitting but I do not know how. I have tried adding the directories and files from the error message, but no matter what I do, the error message seems to be the same (22 packages).
In the workdir, the image and package folders seem to be identical, and the package-split folder contains mvimpact-acquire-base (with only the /etc folder inside, but no /opt), while the mvimpact-acquire-base-dev, mvimpact-acquire-base-doc, mvimpact-acquire-base-src folders are all empty.
Can anyone help me understand this problem better or suggest a potential solution regarding the FILES variable and the error message.
I think the other error messages about stripping are not at issue here, but if you can share additional info it would be great.
Below are the contents of some WORKDIR(/work/raspberrypi4_64-poky-linux/mvimpact-acquire-base/2.45.0-r0) subdirectories.
image/
├── etc
│   ├── ld.so.conf.d
│   │   └── acquire.conf
│   └── profile.d
│   └── acquire.sh
└── opt
└── mvIMPACT_Acquire
├── data
│   └── logs
│   └── mvDebugFlags.mvd
├── lib
│   ├── libmvDeviceManager.so -> libmvDeviceManager.so.2.45.0
│   ├── libmvDeviceManager.so.2 -> libmvDeviceManager.so.2.45.0
│   ├── libmvDeviceManager.so.2.45.0
│   ├── libmvPropHandling.so -> libmvPropHandling.so.2.45.0
│   ├── libmvPropHandling.so.2 -> libmvPropHandling.so.2.45.0
│   └── libmvPropHandling.so.2.45.0
└── Toolkits
├── FreeImage3160
│   └── bin
│   └── Release
│   └── FreeImage
│   └── arm64
│   └── libfreeimage-3.16.0.so
├── libexpat.so -> libexpat.so.1.6.0
├── libexpat.so.1 -> libexpat.so.1.6.0
└── libexpat.so.1.6.0
14 directories, 13 files
package/
├── etc
│   ├── ld.so.conf.d
│   │   └── acquire.conf
│   └── profile.d
│   └── acquire.sh
└── opt
└── mvIMPACT_Acquire
├── data
│   └── logs
│   └── mvDebugFlags.mvd
├── lib
│   ├── libmvDeviceManager.so -> libmvDeviceManager.so.2.45.0
│   ├── libmvDeviceManager.so.2 -> libmvDeviceManager.so.2.45.0
│   ├── libmvDeviceManager.so.2.45.0
│   ├── libmvPropHandling.so -> libmvPropHandling.so.2.45.0
│   ├── libmvPropHandling.so.2 -> libmvPropHandling.so.2.45.0
│   └── libmvPropHandling.so.2.45.0
└── Toolkits
├── FreeImage3160
│   └── bin
│   └── Release
│   └── FreeImage
│   └── arm64
│   └── libfreeimage-3.16.0.so
├── libexpat.so -> libexpat.so.1.6.0
├── libexpat.so.1 -> libexpat.so.1.6.0
└── libexpat.so.1.6.0
14 directories, 13 files
packages-split/
├── mvimpact-acquire-base
│   └── etc
│   ├── ld.so.conf.d
│   │   └── acquire.conf
│   └── profile.d
│   └── acquire.sh
├── mvimpact-acquire-base-dev
├── mvimpact-acquire-base-doc
└── mvimpact-acquire-base-src
7 directories, 2 files
The issue was new BitBake syntax. I changed the following lines:
INSANE_SKIP_${PN} += "dev-so
FILES_${PN} += "${base_prefix}/${MVIA_SUBDIR}/${MVIA_LIB_SUBDIR}/lib*.so.* \
${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/lib*.so.* \
${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/FreeImage3160/bin/Release/FreeImage/${TARGET}/lib*.so \
${sysconfdir}/profile.d/*.sh \
${sysconfdir}/ld.so.conf.d/*.conf"
to
INSANE_SKIP:${PN} += "dev-so
FILES:${PN} += "${base_prefix}/${MVIA_SUBDIR}/${MVIA_LIB_SUBDIR}/lib*.so.* \
${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/lib*.so.* \
${base_prefix}/${MVIA_SUBDIR}/${TOOLKITS_LIB_SUBDIR}/FreeImage3160/bin/Release/FreeImage/${TARGET}/lib*.so \
${sysconfdir}/profile.d/*.sh \
${sysconfdir}/ld.so.conf.d/*.conf"
So basically, instead of underscores it now uses colons.

bitbake not calling my do_install

I have a recipe that should be copying a runlevel script into /etc/init.d and creating a symbolic link to it from /etc/rc5 However the do_install function does not appear to be being called.
Below the structure of my layer. The problem bb file is init-wifi.bb at the bottom of the tree. The other recipes with bbappends work fine.
.
├── conf
│ └── layer.conf
├── recipes-connectivity
│ ├── alsa
│ │ ├── alsa-lib
│ │ └── alsa-lib_1.0.29.bbappend
│ └── wpa-supplicant
│ ├── wpa-supplicant
│ │ ├── wpa_supplicant.conf
│ │ └── wpa_supplicant.conf-sane
│ └── wpa-supplicant_2.4.bbappend
├── recipes-core
│ ├── base-files
│ │ ├── base-files
│ │ │ └── profile
│ │ └── base-files_%.bbappend
│ └── init-ifupdown
│ ├── init-ifupdown-1.0
│ │ └── interfaces
│ └── init-ifupdown_1.0.bbappend
└── recipes-my
└── init-wifi
├── files
│ └── wifi_start.sh
└── init-wifi.bb
Below is the init-wifi.bb recipe:
SUMMARY = "x"
LICENSE = "CLOSED"
#PR = "r0"
SRC_URI += "file://wifi_start.sh"
#INITSCRIPT_NAME = "wifi_start.sh"
#INITSCRIPT_PARAMS = "defaults 90"
do_install() {
install -d ${D}${sysconfdir}/init.d
install -d ${D}${sysconfdir}/rcS.d
install -d ${D}${sysconfdir}/rc1.d
install -d ${D}${sysconfdir}/rc2.d
install -d ${D}${sysconfdir}/rc3.d
install -d ${D}${sysconfdir}/rc4.d
install -d ${D}${sysconfdir}/rc5.d
install -m 0755 ${WORKDIR}/wifi_start.sh ${D}${sysconfdir}/init.d/
ln -sf ${D}${syscondir}/init.d/wifi_start.sh {D}${sysconfdir}/rc5.d/S90wifi_start.sh
}
If I introduce errors to the bb file outside the do_install function I get errors wen invoking bitbake, so I know my recipe file is being found and parsed. However If i introduce errors inside the do_install function it is not being called. Additionally I don't see the script being installed into the work or image directories (after removing intentional debugging errors).
If I force bitbake to run the recipe with 'bitbake -c install init-wifi', it will install the files "work/image" directories:
tmp/work/cortexa7hf-vfp-neon-poky-linux-gnueabi/initwifi/0.0-r0/image/etc/init.d/wifi_start.sh
tmp/work/cortexa7hf-vfp-neon-poky-linux-gnueabi/initwifi/0.0-r0/wifi_start.sh
However when my image is built and installed on my module, the script and links are not there.
Been struggling with this for a couple of days and searches haven't produced much help either.
Any ideas?
Thanks!
You didn't add your new recipe to your image recipe.
IMAGE_INSTALL_append = " init-wifi "
EDIT
I used to do the same and it works well.
Differences I can see is:
SRC_URI += "file://wifi_start.sh"
should be
SRC_URI = "file://wifi_start.sh"
Beacuse you are creating SRC_URI, not adding to an existing one.
I used to add md5 checksum for each file I use.
You should add
FILES_${PN} += "${sysconfdir}/profile.d"
FILES_${PN} += "${sysconfdir}/rcS.d"
FILES_${PN} += "${sysconfdir}/rc1.d"
FILES_${PN} += "${sysconfdir}/rc2.d"
FILES_${PN} += "${sysconfdir}/rc3.d"
FILES_${PN} += "${sysconfdir}/rc4.d"
FILES_${PN} += "${sysconfdir}/rc5.d"
From the Yocto man
FILES
The list of directories or files that are placed in packages.
To use the FILES variable, provide a package name override that identifies the resulting package. Then, provide a space-separated list of files or paths that identify the files you want included as part of the resulting package. Here is an example:
FILES_${PN} += "${bindir}/mydir1/ ${bindir}/mydir2/myfile"
LPS,
Thanks for the help and suggestions. With them I was able to get it to install the file and create the link.
I made the suggested changes to my init-wifi.bb file:
SUMMARY = "x"
LICENSE = "CLOSED"
#PR = "r0"
SRC_URI = "file://wifi_start.sh"
FILES_${PN} += "${sysconfdir}/init.d"
FILES_${PN} += "${sysconfdir}/rcS.d"
FILES_${PN} += "${sysconfdir}/rc1.d"
FILES_${PN} += "${sysconfdir}/rc2.d"
FILES_${PN} += "${sysconfdir}/rc3.d"
FILES_${PN} += "${sysconfdir}/rc4.d"
FILES_${PN} += "${sysconfdir}/rc5.d"
do_install() {
install -d ${D}${sysconfdir}/init.d
install -d ${D}${sysconfdir}/rcS.d
install -d ${D}${sysconfdir}/rc1.d
install -d ${D}${sysconfdir}/rc2.d
install -d ${D}${sysconfdir}/rc3.d
install -d ${D}${sysconfdir}/rc4.d
install -d ${D}${sysconfdir}/rc5.d
install -m 0755 ${WORKDIR}/wifi_start.sh ${D}${sysconfdir}/init.d/
ln -sf ${D}${syscondir}/init.d/wifi_start.sh ${D}${sysconfdir}/rc5.d/S90wifi_start.sh
}
Additionally I had to add the suggested line below to meta-mylayer/conf/layer.conf
IMAGE_INSTALL_append = " init-wifi "
I am getting a warning:
WARNING: QA Issue: Symlink /etc/rc5.d/S90wifi_start.sh in init-wifi points to TMPDIR [symlink-to-sysroot]
So I think I'll revisit how I'm doing the links, but I am 'off top dead-center' and moving forward again.
Thanks!
-Steve

How to automatically create README.md markdown of directory tree listing

I want to create markdown with a directory tree listing automatically, in order to be shown in online repos like GitHub in every directory.
So, given that I'm going to use the Linux tree command that can be installed on MacOS X using brew install tree (see here for details), I came out with this solution:
tree --dirsfirst --noreport -I README.md > README.md;sed -i '' '1s/^/```/' README.md;echo \ >> README.md; sed -i '' -e '$s/$/```/' README.md
where the first sed -i '' '1s/^/```/' README.md is prepending the ``` chars – see Mastering (Github) Markdown for details about supported markdown.
The echo echo \ >> README.md; is adding a newline. Note here that I'm not using the sed equivalent sed -i '' -e '$a\' filename since this only adds a newline when it does not exists due to the $a pattern (see here).
While the last sed -i '' -e '$s/$/```/' README.md is adding a trailing ``` to the file.
The tree command tree --dirsfirst --noreport -I README.md is going to exclude some patterns, put directory first, ignore reporting file and dir count.
The result is going to be something like the following:
```.
├── bin
│   ├── Debug
│   │   ├── SampleLibrary.jar
│   │   ├── cooper.jar
│   │   ├── sugar.data.jar
│   │   ├── sugar.jar
│   │   └── swift.jar
│   └── Release
│   ├── SampleLibrary.jar
│   ├── cooper.jar
│   ├── sugar.data.jar
│   ├── sugar.jar
│   └── swift.jar
├── obj
│   ├── Debug
│   │   └── Android
│   │   ├── ClassLibrary2.elements.FilesWrittenAbsolute.txt
│   │   └── samplelibrary.jar
│   └── Release
│   └── Android
│   ├── ClassLibrary2.elements.FilesWrittenAbsolute.txt
│   └── samplelibrary.jar
├── ClassLibrary2.elements
└── ClassLibrary2.elements.user
```
You can see this markdown README.md here.
This solution is not so efficient and it is limited to -I pattern options of tree to filter out unwanted dirs (let's say build directories) or file names, etc. Also it does not work properly to update an existing README.md markdown.
The solution should work on MacOS X (where sed has some differences to that on Linux).
One way to get source code markup is to indent everything by four spaces:
tree --dirsfirst --noreport -I README.md | sed 's/^/ /' > README.md
To do it your way, adding a new first and last line with ``` on each, we can do
tree --dirsfirst --noreport -I README.md |
sed '1s/^/```'$'\n''/;$s/$/'$'\n''```/' > README.md
where inserting a newline in the replacement string is done with a C-style escape. Alternatively, we can use "$(printf '\n')":
tree --dirsfirst --noreport -I README.md |
sed '1s/^/```'"$(printf '\n')"'/;$s/$/'"$(printf '\n')"'```/' > README.md
These should both work with the sed on macOS.
With GNU sed, it would be a little simpler:
tree --dirsfirst --noreport -I README.md |
sed '1s/^/```\n/;$s/$/\n```/' > README.md
Have a look at this python module (I am the author).
It generats descriptive directory trees dynamically, and it has a markdown export format like the following:
.
├── example_folder\
│ ├── first_subfolder\ a documented folder
│ │ ├── sub-sub1\
│ │ │ └── file3.sh this is file 3
│ │ ├── sub-sub2\
│ │ │ └── file4.cpp this is file 4
│ │ └── random_file.rdm a documented file
│ ├── second_subfolder\ _a documented folder
│ ├── a_text_file.txt a text file
│ ├── my_javascript.js this is file 1
│ └── test.py a python script
└── README.md The main readme\
and an ASCII one of course
.
├── example_folder\
│ ├── first_subfolder\ (a documented folder)
│ │ ├── sub-sub1\
│ │ │ └── file3.sh (this is file 3)
│ │ ├── sub-sub2\
│ │ │ └── file4.cpp (this is file 4)
│ │ └── random_file.rdm (a documented file)
│ ├── second_subfolder\ (a documented folder with no documented files)
│ ├── a_text_file.txt (a text file)
│ ├── my_javascript.js (this is file 1)
│ └── test.py (a python script)
└── README.md (The main readme)
Create your markdown in VSCode and add Ascii Tree Generator extension from Marketplace.

How do I create a multi-module distribution?

I want to create my own Perl module, but the problem is that it contain multiple .pm files. The structure is:
lib
├── A_Z.pm
└── T_test
├── A.pm
├── B.pm
├── C.pm
├── D.pm
└── E.pm
I used h2xs -XA -n T_test::A T_test::B T_test::C T_test::D T_test::E. It compiled only A.pm; the other B.pm, C.pm, D.pm, E.pm are not considered. Is there any solution to execute all the .pm file at the same time?
Use Module::Starter::PBP instead.
$ module-starter --builder=Module::Build --module=A_Z,T_test::{A,B,C,D,E}
Added to MANIFEST: Build.PL
Added to MANIFEST: Changes
Added to MANIFEST: lib/A_Z.pm
Added to MANIFEST: lib/T_test/A.pm
Added to MANIFEST: lib/T_test/B.pm
Added to MANIFEST: lib/T_test/C.pm
Added to MANIFEST: lib/T_test/D.pm
Added to MANIFEST: lib/T_test/E.pm
Added to MANIFEST: MANIFEST
Added to MANIFEST: README
Added to MANIFEST: t/00.load.t
Created starter directories and files
$ tree A_Z
A_Z
├── Build.PL
├── Changes
├── lib
│ ├── A_Z.pm
│ └── T_test
│ ├── A.pm
│ ├── B.pm
│ ├── C.pm
│ ├── D.pm
│ └── E.pm
├── MANIFEST
├── README
└── t
└── 00.load.t
3 directories, 11 files
You don't have to do anything special. Just makes sure all the files are listed in MANIFEST as usual. Both ExtUtils::MakeMaker and Module::Build consider all .pm to be modules to install.