diff --git a/neurodocker/templates/freesurfer.yaml b/neurodocker/templates/freesurfer.yaml index 76f7f064..21f6ddbe 100644 --- a/neurodocker/templates/freesurfer.yaml +++ b/neurodocker/templates/freesurfer.yaml @@ -7,6 +7,9 @@ binaries: - version optional: install_path: /opt/freesurfer-{{ self.version }} + license_base64: "" + license_path: "/opt" + license_file: license.txt exclude_paths: | average/mult-comp-cor lib/cuda @@ -26,6 +29,10 @@ binaries: "7.3.2": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.3.2/freesurfer-linux-centos7_x86_64-7.3.2.tar.gz "7.3.1": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.3.1/freesurfer-linux-centos7_x86_64-7.3.1.tar.gz "7.3.0": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.3.0/freesurfer-linux-centos7_x86_64-7.3.0.tar.gz + "7.2.0-centos6": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.2.0/freesurfer-CentOS6-7.2.0-1.x86_64.rpm + "7.2.0-centos7": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.2.0/freesurfer-CentOS7-7.2.0-1.x86_64.rpm + "7.2.0-centos8": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.2.0/freesurfer-CentOS8-7.2.0-1.x86_64.rpm + "7.2.0-deb": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.2.0/freesurfer_7.2.0_amd64.deb "7.2.0": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.2.0/freesurfer-linux-centos6_x86_64-7.2.0.tar.gz "7.1.1": https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/7.1.1/freesurfer-linux-centos6_x86_64-7.1.1.tar.gz "7.1.1-min": https://dl.dropbox.com/s/c3earkfhhvdyuo4/freesurfer-7.1.1-min.tgz @@ -69,7 +76,7 @@ binaries: # default freesurfer options FS_OVERRIDE: "0" FIX_VERTEX_AREA: "" - FSF_OUTPUT_FORMAT: "nii.gz# mni env requirements" + FSF_OUTPUT_FORMAT: "nii.gz" # mni environment requirements MINC_BIN_DIR: "{{ self.install_path }}/mni/bin" MINC_LIB_DIR: "{{ self.install_path }}/mni/lib" @@ -79,15 +86,287 @@ binaries: PERL5LIB: "{{ self.install_path }}/mni/share/perl5" instructions: | {{ self.install_dependencies() }} - echo "Downloading FreeSurfer ..." - mkdir -p {{ self.install_path }} - curl -fL {{ self.urls[self.version] }} \ - | tar -xz -C {{ self.install_path }} --owner root --group root --no-same-owner --strip-components 1 {% if self.exclude_paths -%}\ - {%- for exclude_path in self.exclude_paths.split() %} - {% if not loop.last -%} - --exclude='{{ exclude_path }}' \ + {%- if self.version in ["6.0.0-min", "6.0.0", "6.0.1", "7.1.0", "7.1.1-min", "7.1.1", "7.2.0"] %} + echo "Downloading FreeSurfer ..." + mkdir -p {{ self.install_path }} + curl -fL {{ self.urls[self.version] }} \ + | tar -xz -C {{ self.install_path }} --owner root --group root --no-same-owner --strip-components 1 {% if self.exclude_paths -%}\ + {%- for exclude_path in self.exclude_paths.split() %} + {% if not loop.last -%} + --exclude='{{ exclude_path }}' \ + {%- else -%} + --exclude='{{ exclude_path }}' + {%- endif -%} + {%- endfor %} + {%- endif %} + {%- else -%} + {%- if self.version in ["deb"] %} + apt-get update -qq + apt-get install --fix-missing -y \ + apt-file apt-utils apt-transport-https dpkg-dev debhelper devscripts librpm-dev rpm + curl -o /stage/freesurfer_amd64.deb {{ self.urls[self.version] }} + dpkg -i /stage/freesurfer_amd64.deb + DEBIAN_FRONTEND=noninteractive apt-get -f -y install + apt-get clean + rm -rf /var/lib/apt/lists/* + sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' {%- else -%} - --exclude='{{ exclude_path }}' + # todo: wip + yum -y update --exclude=filesystem* + yum -y install dnf-plugins-core epel-release + yum -y config-manager --set-enabled powertools + yum -y update --exclude=filesystem* {%- endif -%} - {%- endfor %} - {%- endif %} + echo "{{ self.pkg_manager }}" + {%- endif -%} + +source: + arguments: + optional: + repo: https://github.com/freesurfer/freesurfer + branch: "dev" + commit: "" + license_base64: "" + license_path: "/opt" + license_file: license.txt + # The subject dir is not in /opt, like everything else FreeSurfer-related + # Because if we want to run `neurodocker minify` we want to operate over the `/opt` + # dir and have the subjects dir excluded + subjects_dir: "/ext/fs-subjects" + infant_model_dir: "/opt/fs-infant-model" + infant_model_s3: "" + infant_model_s3_region: "" + pkg_url: http://surfer.nmr.mgh.harvard.edu/pub/data/fspackages/prebuilt/centos7-packages.tar.gz + annex: https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/repo/annex.git + # set to niftyreg.yaml default + niftyreg_path: /opt/niftyreg-master + minimal: "ON" + build_guis: "OFF" + infant_module: "OFF" + install_python_deps: "OFF" + distribute_fspython: "OFF" + martinos_build: "OFF" + samseg_atlas_build: "OFF" + git_annex_get: "ON" + dev_tools: "OFF" + fortran_libs: /usr/lib/gcc/x86_64-linux-gnu/5/libgfortran.so + # --- Probably don't have to change args below here --- + # Were using a source path of `/stage` instead of `/temp` in case we want to + # keep the source dir around for debugging (dev_tools=ON) with singularity containers + # (which bind mount the `/tmp` dir by default) + source_path: /stage/freesurfer/freesurfer-{{ self.branch }} + pkg_path: /opt/freesurfer-pkg + install_path: /opt/freesurfer-{{ self.branch }} + cxx_flags: "-msse2 -mfpmath=sse -fPIC -fpermissive" + c_flags: "-msse2 -mfpmath=sse -fPIC" + cmake_opts: -DCMAKE_INSTALL_PREFIX={{ self.install_path }} -DBUILD_GUIS={{ self.build_guis }} -DMINIMAL={{ self.minimal }} -DINFANT_MODULE={{ self.infant_module }} -DFS_PACKAGES_DIR={{ self.pkg_path }} -DGFORTRAN_LIBRARIES={{ self.fortran_libs }} -DINSTALL_PYTHON_DEPENDENCIES={{ self.install_python_deps }} -DDISTRIBUTE_FSPYTHON={{ self.distribute_fspython }} -DMARTINOS_BUILD={{ self.martinos_build }} -DGEMS_BUILD_EXECUTABLES={{ self.samseg_atlas_build }} -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DCMAKE_CXX_COMPILER_AR=/usr/bin/ar -DCMAKE_CXX_COMPILER_RANLIB=/usr/bin/ranlib -DCMAKE_CXX_FLAGS="{{ self.cxx_flags }}" -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_C_COMPILER_AR=/usr/bin/ar -DCMAKE_C_COMPILER_RANLIB=/usr/bin/ranlib -DCMAKE_C_FLAGS="{{ self.c_flags }}" + make_opts: -j4 + env: + FS_LICENSE: "{{ self.license_path }}/{{ self.license_file }}" + SUBJECTS_DIR: "{{ self.subjects_dir }}" + FS_INFANT_MODEL: "{{ self.infant_model_dir }}" + SSCNN_MODEL_DIR: "{{ self.infant_model_dir }}/sscnn_skullstrip" + # From https://github.com/freesurfer/freesurfer/blob/54018f7d6f620d6288b28f50e14a0a4ba421757c/Dockerfile#L20-L42 + OS: "Linux" + PATH: "{{ self.niftyreg_path }}/bin:{{ self.pkg_path }}/mni/current/bin:{{ self.install_path }}/bin:{{ self.install_path }}/fsfast/bin:{{ self.install_path }}/tktools:{{ self.install_path }}/mni/bin:$PATH" + FREESURFER_HOME: "{{ self.install_path }}" + FREESURFER: "{{ self.install_path }}" + PERL5LIB: "{{ self.install_path }}/mni/share/perl5" + LOCAL_DIR: "{{ self.install_path }}/local" + FSFAST_HOME: "{{ self.install_path }}/fsfast" + FMRI_ANALYSIS_DIR: "{{ self.install_path }}/fsfast" + FSF_OUTPUT_FORMAT: "nii.gz" + FUNCTIONALS_DIR: "{{ self.install_path }}/sessions" + PYTHONPATH: "{{ self.install_path }}/python/packages" + # default freesurfer options + FS_OVERRIDE: "0" + FIX_VERTEX_AREA: "" + # To supress a `Could not set locale` error when `mri_convert` is run in recon-all + FS_DISABLE_LANG: "1" + # Just in case: https://github.com/freesurfer/freesurfer/issues/776 + FS_TIME_ALLOW: "0" + # mni environment requirements + MINC_BIN_DIR: "{{ self.pkg_path }}/mni/current/bin" + MINC_LIB_DIR: "{{ self.pkg_path }}/mni/current/lib" + MNI_DIR: "{{ self.pkg_path }}/mni" + MNI_DATAPATH: "{{ self.pkg_path }}/mni/current/data" + MNI_PERL5LIB: "{{ self.pkg_path }}/mni/current/share/perl5" + PERL5LIB: "{{ self.pkg_path }}/mni/current/share/perl5" + # infant model setup, see: + # - https://github.com/pwighton/fs-docker/blob/f691232312e97f2ea87b8ae45bfd5c365e2bc91c/baby/dockerfile#L85 + # - https://surfer.nmr.mgh.harvard.edu/pub/dist/freesurfer/infant/freesurfer-linux-centos7_x86_64-infant.tar.gz + # This temporary and should migrate somewhere else + CNYBCH_TEMPLATE_SUBJECTS_DIR: "{{ self.infant_model_dir }}/CNYBCH" + CNYBCH_SUBJECTS: "Template1 Template2 Template3 Template4 Template5 Template6 Template7 Template8 Template9 Template10 Template11 Template12 Template13 Template14 Template15 Template16 Template17 Template18 Template19 Template20 Template21 Template22 Template23 Template24 Template25 Template26" + # age at MRI scan (mo) + CNYBCH_AGES: "9 7 6 5 18 12 0 0 3 8 10 10 18 4 2 14 3 16 0 12 0 15 5 17 16 0" + CNYBCH_GMWM_SUBJECTS: "Template5 Template6 Template8 Template10 Template13 Template18 Template20 Template22" + # mo + CNYBCH_GMWM_AGES: "18 12 0 8 18 16 12 15" + CNYBCH_NEONATES: "Template7 Template8 Template19 Template21 Template26" + # days (first two computed from weeks) + CNYBCH_NEONATEAGES: "3 4 1 2 4" + CNYBCH_AROUNDONE: "Template11 Template12 Template6 Template20 Template16" + #mo + CNYBCH_AROUNDONEAGES: "10 10 12 12 14" + dependencies: + apt: + - ca-certificates + - cmake + - git + - git-annex + - make + - bc + - binutils + - bzip2 + - coreutils + - curl + - g++-4.8 + - gcc-4.8-base + - gcc-4.8 + - gfortran-4.8 + - libbz2-dev + - libfreetype6-dev + - libgfortran-4.8-dev + - libglib2.0-0 + - libglu1-mesa-dev + - libgomp1 + - libjpeg62-dev + - libopenblas-dev + - libssl-dev + - libsqlite3-dev + - libtool + - libtool-bin + - libx11-dev + - libxaw7-dev + - libxi-dev + - libxml2-utils + - libxmu-dev + - libxmu-headers + - libxmu6 + - libxt-dev + - libxt6 + - perl + - sudo + - tar + - tcsh + - unzip + - uuid-dev + - vim-common + - wget + yum: + - ca-certificates + - cmake + - gcc-c++ + - git + - make + instructions: | + {{ self.install_dependencies() }} + {%- if self.license_base64 is defined and self.license_base64|length %} + echo " fs license (base64): "{{ self.license_base64 }} + mkdir -p {{ self.license_path }} + echo {{ self.license_base64 }} | base64 -d > {{ self.license_path }}/{{ self.license_file }} + {% endif -%} + # git config + # ------------------------------------------------------------------------- + git config --global user.email "CI@example.com" + git config --global user.name "CI" + # Switch to gcc 4.8 + # ------------------------------------------------------------------------- + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100 + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100 + update-alternatives --install /usr/bin/gfortran gfortran /usr/bin/gfortran-4.8 100 + # Make a staging directory, we don't want to use /tmp since it doesn't play nice with singularity + # See: https://github.com/ReproNim/neurodocker/issues/246 + # ------------------------------------------------------------------------- + mkdir -p /stage + # Python v3.8.9 + # ------------------------------------------------------------------------- + curl -sSL --retry 5 https://www.python.org/ftp/python/3.8.9/Python-3.8.9.tgz | tar -xz -C /stage + cd /stage/Python-3.8.9 + ./configure + make + make install + cd / + rm -rf /stage/Python-3.8.9 + pip3 install -q --no-cache-dir -U pip + pip3 install -q --no-cache-dir wheel + sync + # Cmake v3.19.0 (must be > v3.6.8) + # ------------------------------------------------------------------------- + wget https://github.com/Kitware/CMake/releases/download/v3.19.0/cmake-3.19.0-Linux-x86_64.sh -q -O /stage/cmake-install.sh + chmod u+x /stage/cmake-install.sh + mkdir -p /usr/share/cmake-3.19.0 + /stage/cmake-install.sh --skip-license --prefix=/usr/share/cmake-3.19.0 + CMAKE_BINPATH=`which cmake` + rm -rf $CMAKE_BINPATH + ln -s /usr/share/cmake-3.19.0/bin/cmake $CMAKE_BINPATH + rm /stage/cmake-install.sh + # Compile FreeSurfer + # ---------------------------------------------------------------- + mkdir -p {{ self.pkg_path }} + mkdir -p {{ self.install_path }} + wget -q -c {{ self.pkg_url }} -O - | tar -xz -C {{ self.pkg_path }} + mv {{ self.pkg_path }}/packages/* {{ self.pkg_path }} + rm -rf {{ self.pkg_path }}/packages + echo "Cloning..." + echo " remote: "{{ self.repo }} + echo " branch: "{{ self.branch }} + echo " commit: "{{ self.commit }} + echo " destination: "{{ self.source_path }} + git clone --branch {{ self.branch }} {{ self.repo }} {{ self.source_path }} + {%- if self.commit is defined and self.commit|length %} + cd {{ self.source_path }} + git checkout {{ self.commit }} + {% endif -%} + cd {{ self.install_path }} + cmake {{ self.cmake_opts }} {{ self.source_path }} + make {{ self.make_opts }} + # Install python reqs + # ---------------------------------------------------------------- + {%- if self.infant_module.lower() in ["on", "true", "1", "y"] %} + pip3 install -r {{ self.source_path }}/infant/docker/requirements.txt + ln -s {{ self.install_path }}/bin/infant-container-entrypoint.bash /bin/infant-container-entrypoint.bash + ln -s {{ self.install_path }}/bin/infant-container-entrypoint-aws.bash /bin/infant-container-entrypoint-aws.bash + {% else %} + pip3 install -r {{ self.source_path }}/python/requirements.txt + {% endif -%} + # Setup annex remote + # ---------------------------------------------------------------- + cd {{ self.source_path }} + git remote add datasrc {{ self.annex }} + git fetch datasrc + git annex enableremote datasrc || true + # Pull from annex and install + # (`make install` will fail if `git annex get` isn't run) + # ---------------------------------------------------------------- + {%- if self.git_annex_get.lower() in ["on", "true", "1", "y"] %} + cd {{ self.source_path }} + git annex get -q . + git annex unlock + cd {{ self.install_path }} + make install + {% endif -%} + # Setup mount points + # ---------------------------------------------------------------- + mkdir -p {{ self.subjects_dir }} + mkdir -p {{ self.infant_model_dir }} + # Dev tools + # - jupyter relies on `libsqlite3-dev` being present before python is compiled + # - the /stage dir isn't deleted (to facilitate recompile/install/test) + # - awscli is installed (todo, best place for this?) + # ---------------------------------------------------------------- + {%- if self.dev_tools.lower() in ["on", "true", "1", "y"] %} + pip3 install jupyter + cd /stage + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" + unzip awscliv2.zip + ./aws/install + rm -rf /stage/aws + rm -rf /stage/awscliv2.zip + {%- if self.infant_model_s3 is defined and self.infant_model_s3|length and self.infant_model_s3_region is defined and self.infant_model_s3_region|length %} + aws s3 cp --no-sign-request --region={{ self.infant_model_s3_region }} --recursive --include '*' {{ self.infant_model_s3 }} {{ self.infant_model_dir }}/ + {% endif %} + {% else %} + rm -rf /stage + {% endif -%}