Hi all,
I have this I am in the process of writing to create custom images for nano:
#!/usr/bin/env python3
# Copyright 2019 Michael de Gans
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sqlite3
import subprocess
def choose_jetpack(jetpacks):
print('Multiple JetPacks found. Please choose from the following:')
for i, jetpack in enumerate(jetpacks):
print(f'{i}: {jetpack[0]} for hardware: {jetpack[1]} at {jetpack[2]}')
choice = None
while choice not in range(len(jetpacks)):
try:
choice = int(input(f'Choice (0 to {len(jetpacks) - 1})?'))
except ValueError:
pass
return jetpacks[choice]
def get_jetpack_home():
dbfile = os.path.join(os.path.expanduser('~'), '.nvsdkm', 'sdkm.db')
if not os.path.isfile(dbfile):
raise RuntimeError(f'Sdk manager does not appear installed. '
f'Cannot find db at {dbfile}')
# TODO: add good, persistent, install url to error
with sqlite3.connect(dbfile) as conn: # type: sqlite3.Connection
cur = conn.cursor()
cur.execute(
"SELECT title, targetOS, targetHW, targetImageDir FROM bundles")
jetpacks = []
for title, target_os, target_hw, target_image_dir in cur:
if target_hw != 'host' and target_os == 'Linux':
jetpacks.append((title, target_hw, target_image_dir))
if not jetpacks:
raise RuntimeError(
'No installed bundles found. Please run SDKManager to install '
'the bundles for your hardware.')
if len(jetpacks) > 1:
jetpack = choose_jetpack(jetpacks)
else:
jetpack = jetpacks[0]
return jetpack[2] # (target_image_dir)
def sudo_debootstrap(rootfs,
version='bionic',
include=None,
components=('restricted', 'universe', 'multiverse'),
):
print('Running debootstrap on rootfs...')
cmd = ['sudo', 'debootstrap', '--arch=arm64', '--foreign', '--verbose',
'--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg']
# if any packages to include or components to add, add the options:
if include:
if len(include) == 1 and os.path.isfile(include):
with open(include[0]) as f:
include = {line.strip() for line in f}
cmd.append(f'--include={",".join(include)}')
if components:
cmd.append(f'--components={",".join(components)}')
subprocess.run((*cmd, version, rootfs)).check_returncode()
def main(outfile, image_size='8G', **kwargs):
# some important paths
jetpack_home = get_jetpack_home()
l4t_dir = os.path.join(jetpack_home, 'Linux_for_Tegra')
rootfs_dir = os.path.join(l4t_dir, 'rootfs')
# following this documentation more or less:
# https://docs.nvidia.com/jetson/l4t/Tegra%20Linux%20Driver%20Package%20Development%20Guide/rootfs_custom.html
# make sure rootfs dir exists, if it doesn't, something is very wrong
if not os.path.isdir(rootfs_dir):
raise RuntimeError(f'rootfs directory not found at {rootfs_dir}')
# clean out rootfs folder:
delete_cmd = f'sudo rm -rf {rootfs_dir}/*'
while os.listdir(rootfs_dir):
response = input(f'{rootfs_dir} directory is not empty. Would you like '
f'to execute "{delete_cmd}" y/n ?').lower()
if response == 'y':
subprocess.run(delete_cmd, shell=True).check_returncode()
elif response == 'n':
print(
f'{rootfs_dir} must be empty to continue. The delete command can'
f' be run manually with "{delete_cmd}"')
sudo_debootstrap(rootfs_dir, **kwargs)
print('Applying binaries...')
subprocess.run(
os.path.join(l4t_dir, 'apply_binaries.sh')).check_returncode()
# DEBS install here...
# building the image
print('Building flashable image...')
img_script = os.path.join(l4t_dir, 'create-jetson-nano-sd-card-image.sh')
subprocess.run(
(img_script, '-o', outfile, '-s', image_size, '-r', '200')
).check_returncode()
print('Done... Image ready for sd card flash.')
def cli_main():
import argparse
component_choices = {'restricted', 'universe', 'multiverse'}
# TODO: test with xenial
valid_ubuntu_versions = {'bionic'}
ap = argparse.ArgumentParser(
description='Builds a custom Ubuntu image for NVIDIA Jetson Nano')
ap.add_argument('outfile', help='image filename to write out to.')
ap.add_argument('--version', default='bionic', choices=valid_ubuntu_versions,
help='version of Ubuntu to bootstrap')
ap.add_argument('--apt-packages', nargs='+', dest='include',
help='list of apt packages / meta-packages to be installed'
'(text file with list of apt packages also accepted)')
ap.add_argument('--components', nargs='+', choices=component_choices,
default=component_choices,
help='this sets which apt sources are used (default, all).'
'see debootstrap --components flag)')
# todo: automatically calculate this:
ap.add_argument('--image-size', default='8G',
help='-s option for create-jetson-nano-sd-card-image.sh')
main(**vars(ap.parse_args()))
if __name__ == '__main__':
cli_main()
… and it would be great to have some help help where it says “# DEBS install here…”
SDKM downloads a bunch of .deb files to a download path, but so far as I can find, that path is not stored in sdkm.db with all the other sdkm details.
I can parse it from the log, but that’s ugly and the files could have changed. I could guess at it but same problem.
The only other way I can think of to clean .deb files is to use the stored token in the db along with something like requests_oauth and get the debs from the urls in the json, but that’s a lot of work (although I did start, see below for snippet).
...
MAIN_REPO_URL = 'https://developer.download.nvidia.com/sdkmanager/sdkm-config/main/sdkml1_repo.json'
def get_jetson_linux_repo_json(session):
main_repo_config = session.get(MAIN_REPO_URL).json()
product_categories = main_repo_config['productCategories']
for category in product_categories:
if category['categoryName'] == 'Jetson':
jetson_category = category
break
else:
raise RuntimeError('Jetson category not found product categories.')
product_lines = jetson_category['productLines']
for product_line in product_lines:
if product_line['targetOS'] == 'Linux':
release_url = product_line['releasesIndexURL']
break
else:
raise RuntimeError('Linux not found in product lines for Jetson.')
# figure out the absolute path to the jetson json and return it
main_url = urlparse(MAIN_REPO_URL) # 6 tuple of Text
scheme, netloc, path, params, query, fragment, = main_url
dirname = os.path.dirname(os.path.dirname(path))
# there may be a better way to handle the ../ but it works:
json_path = os.path.join(dirname, release_url.lstrip('../'))
jetson_json_url = urlunparse(( # same 6 tuple of text with modified path
scheme, netloc, json_path, params, query, fragment))
return session.get(jetson_json_url).json()
def get_jetpack_json(session):
"""gets jetpack json from an authorzed session"""
repo_json = get_jetson_linux_repo_json(session)
latest_release = repo_json['releases'][-1]
return session.get(latest_release['compRepoURL']).json()
...
Is there a tarball somewhere with .deb files exclusively for arm64 containing things like cudnn and libvisionworks, or better yet, can apply_binaries.sh install this stuff as an option?