This article is about the computing library developed by CERN. For the part of a plant, see Root. For other uses, see Root (disambiguation).
ROOT is available on Linux, Mac, and (as a beta release) on Windows.
The latest stable ROOT release is
6.28/04 (about ROOT versioning scheme).
Several particle physics collaborations have written software based on ROOT, often in favor of using more generic solutions (e.g. using ROOT containers instead of STL).

Это первая статья по в данной теме, всего их планируется 3:
- * Создание root application из вашего существующего проекта, добавление в него 3 микро-приложения (vue, react, angular)
- Общение между микро-приложениями
- Работа с git (deploy, обновления)

Пока в Стокгольме проходила 118-я Нобелевская неделя, в офисе разработки статического анализатора кода PVS-Studio готовился обзор кода проекта ROOT, используемого в научных исследованиях для обработки больших данных. Премию за такой код, конечно, не дашь, а вот подробный обзор интересных дефектов кода и лицензию для полной проверки проекта разработчики получат.
- «Project Founders». root.cern. Retrieved .
- «ROOT Team». root.cern. Retrieved .
- «ROOT Version 6.28 Release Notes». root.cern. Retrieved .
- Buckley, Andy (2007-08-27). «The problem with ROOT (a.k.a. The ROOT of all Evil)». InsectNation. Retrieved 2016.
- «Re: Wikipedia criticism about root». Retrieved 2016.
- «RE: Re: Wikipedia criticism about root». Retrieved 2016.
- «What is ROOT?». 1 June 2009. Retrieved 2016.
- «ROOT Version 6.06 Release Notes». 2 June 2015. Retrieved 2016.
Wikibooks has a book on the topic of: ROOT
- The ROOT System Home Page
- Image galleries
- ROOT User’s Guide
- ROOT Reference Guide
- ROOT Forum
- The RooFit Toolkit for Data Modeling, an extension to ROOT to facilitate maximum likelihood fits
- The Toolkit for Multivariate Data Analysis with ROOT (TMVA) is a ROOT-integrated project providing a machine learning environment for the processing and evaluation of multivariate classification, both binary and multi class, and regression techniques targeting applications in high-energy physics (here or here).
- Matplotlib – a plotting and analysis system for Python
- SciPy – a scientific data analysis system for Python, based on the NumPy classes
- Perl Data Language – a set of array programming extensions to the Perl programming language
- HippoDraw – an alternative C++-based data analysis system
- Java Analysis Studio – a Java-based AIDA-compliant data analysis system
- R programming language
- AIDA (computing) – open interfaces and formats for particle physics data processing
- Geant4 – a platform for the simulation of the passage of particles through matter using Monte Carlo methods
- PAW
- IGOR Pro
- Scientific Linux
- Scientific computing
- OpenDX
- OpenScientist
- CERN Program Library – legacy program library written in Fortran77, still available but not updated
- Введение
- An open-source data analysis framework used by high energy physics and others.
- An open-source data analysis framework used by high energy physics and others.
- An open-source data analysis framework used by high energy physics and others.
- Минуточку внимания
- Общая часть
- Run in a Docker container
- Создаем микро-приложение REACT (react-app)
- Install via a package manager
- Создаем микро-приложение ANGULAR (angular-app)
- Снова про функцию memset
- Разные предупреждения
- Оглавление
- Ошибки при работе с массивами
- Run on CERN LXPLUS
- Linux package managers
- Fedora
- CentOS
- Arch Linux
- Gentoo
- NixOS/Nix/Nixpkgs
- Ubuntu and Debian-based distributions
- Slackware
- Standalone ROOT
- MacOS package managers
- Homebrew
- Macports
- Nix/Nixpkgs
- Дебют новой диагностики
- Conda
- Зачем это нужно
- Создаем микро-приложение VUE (vue-app)
- Download a pre-compiled binary distribution
- Gentoo Prefix on CVMFS
- Ошибки в условных выражениях
- Snap
- Утечка памяти
- Создание root контейнера
- Build from source
- Некорректный код с указателями
- Complete environment
- Заключение
Введение

ROOT — набор утилит для работы с данными научных исследований. Он обеспечивает все функциональные возможности, необходимые для обработки больших данных, статистического анализа, визуализации и хранения. В основном написан на языке C++. Разработка началась в CERN (Европейская организация по ядерным исследованиям) для исследований по физике высоких энергий. Каждый день тысячи физиков используют ROOT-приложения для анализа своих данных или для моделирования.
PVS-Studio — это инструмент для выявления ошибок и потенциальных уязвимостей в исходном коде программ, написанных на языках С, C++, C# и Java. Работает в 64-битных системах на Windows, Linux и macOS и может анализировать код, предназначенный для 32-битных, 64-битных и встраиваемых ARM платформ.
An open-source data analysis framework used by high energy physics and others.
ROOT is built using the C++17 standard, and it comes bundled along with Python 3.8/PyROOT and Jupyter Notebook / JupyROOT. NumPy, SciPy, Pandas, Matplotlib, Scikit-Learn and Tensorflow/Keras are included with the Python environments.
Snaps are container packages for easy application deployment, designed to be consistent and reliable across different host distributions. They additionally impose some strong sandboxing for security. One caveat of this sandboxing is that this package can only read/write to your $HOME directory, or via specific network protocols. It is highly advised that you place all your data files in your $HOME directory to be accessible.
JupyROOT can be accessed by running root --notebook.
An open-source data analysis framework used by high energy physics and others.
ROOT is built using the C++17 standard, and it comes bundled along with Python 3.8/PyROOT and Jupyter Notebook / JupyROOT. NumPy, SciPy, Pandas, Matplotlib, Scikit-Learn and Tensorflow/Keras are included with the Python environments.
Snaps are container packages for easy application deployment, designed to be consistent and reliable across different host distributions. They additionally impose some strong sandboxing for security. One caveat of this sandboxing is that this package can only read/write to your $HOME directory, or via specific network protocols. It is highly advised that you place all your data files in your $HOME directory to be accessible.
JupyROOT can be accessed by running root --notebook.
An open-source data analysis framework used by high energy physics and others.
ROOT is built using the C++17 standard, and it comes bundled along with Python 3.8/PyROOT and Jupyter Notebook / JupyROOT. NumPy, SciPy, Pandas, Matplotlib, Scikit-Learn and Tensorflow/Keras are included with the Python environments.
Snaps are container packages for easy application deployment, designed to be consistent and reliable across different host distributions. They additionally impose some strong sandboxing for security. One caveat of this sandboxing is that this package can only read/write to your $HOME directory, or via specific network protocols. It is highly advised that you place all your data files in your $HOME directory to be accessible.
JupyROOT can be accessed by running root --notebook.
![]()
Антон
Senior Java Developer.
Доброго времени суток, если кто-нибудь использовал framework ROOT (root.cern.ch/drupal/), поделитесь пожалуйста впечатлениями, стоит ли с ним связываться? Я планирую использовать оттуда некоторую математику и визуализацию для построения графиков. Или стоит поискать другие альтернативы?
-
Вопрос заданболее трёх лет назад
Этот framework я не использовал.
Лично я использую ZedGraph.
Если этот Фреймворк используют ребята из cern, то мне кажется ему можно доверять. Они там очень серьезные вещи шаманят, так что не думаю что библиотека- веник. Хотя найти реальных ее пользователей на хабре- это крайне низкая вероятность
Раньше использовал ROOT, правда не на полную катушку. Остались только хорошие впечатления: все достаточно удобно организовано, есть обширная документация, в т.ч. туториалы, живой форум. Знакомые физики тоже хорошо отзывались, говорят, что не программисту легко разобраться в этом фреймворке.
О других альтернативах не знаю, не было необходимости искать их.
05 июл. 2023, в 03:24
2000 руб./за проект
04 июл. 2023, в 22:15
15000 руб./за проект
04 июл. 2023, в 21:50
50000 руб./за проект
Минуточку внимания
The packages provided by ROOT include those for
The ATLAS experiments presented on 4 July 2012 the status of the Standard Model Higgs search. All the plots presented that day were created in ROOT.
A key feature of ROOT is a data container called tree, with its substructures branches and leaves. A tree can be seen as a sliding window to the raw data, as stored in a file. Data from the next entry in the file can be retrieved by advancing the index in the tree. This avoids memory allocation problems associated with object creation, and allows the tree to act as a lightweight container while handling buffering invisibly.
ROOT is designed for high computing efficiency, as it is required to process data from the Large Hadron Collider‘s experiments estimated at several petabytes per year. As of 2009 ROOT is mainly used in data analysis and data acquisition in particle physics (high energy physics) experiments, and most current experimental plots and results in those subfields are obtained using ROOT.
The inclusion of a C++ interpreter (CINT until version 5.34, Cling from version 6.00) makes this package very versatile as it can be used in interactive, scripted and compiled modes in a manner similar to commercial products like MATLAB.
On July 4, 2012 the ATLAS and CMS LHC’s experiments presented the status of the Standard Model Higgs search. All data plotting presented that day used ROOT.
Общая часть
Для микросервисной архитектуры используем библиотеку single-spa.
В root проект необходимо добавить 3 проекта, используем разные технологии: vue-app, angular-app, react-app (см. п. 4, 5, 6).
Параллельно с созданием этой статьи, я стараюсь внедрить эту архитектуру в боевой проект, над которым в данный момент работаю. Следовательно буду стараться описывать все ошибки, которые у меня возникнут в процессе разработки и их решения.
Root application (далее root) — корень (контейнер) нашего приложения. В него мы будем класть (регистрировать) все наши микросервисы. Если вы уже имеете какой либо проект и хотите реализовать в нем эту архитектуру, то root application будет именно ваш существующий проект, откуда со временем вы будете стараться выгрызать куски вашего приложения, создавать отдельные микросервисы и регистрировать его в этом контейнере.
Такой подход создания root контейнера даст отличную возможность перехода на другой технологию без особой боли.
К примеру мы решили переехать с angular на vue полностью, но проект жирный, и в данный момент приносит много денег бизнесу.
Без микросервисной архитектуры, в мыслях бы этого не могло появиться, только у отчаянных людей, которые верят в единорогов и что мы все — голограмма.
Для того, чтобы перейти на новую технологию в реальности необходимо переписать весь проект, и только тогда мы смогли бы кайфануть от его появления на бою.
Другой вариант, это микросервисная архитектура. Вы можете создать root проект из своего монолита, добавить туда новый проект на том же vue, настроить роуминг в root, готово. Можно лить в бой, постепенно выпиливать с root проекта небольшие кусочки и переносить их в ваш vue микро-проект. В результате в вашем root контейнере останется только те файлы, которые необходимы для импорта вашего нового проекта.
Это можно сделать прям здесь и сейчас, без потерь, крови и главное реально.
В качестве root я буду использовать angular, так как существующий проект был написан именно на нем.
Общий интерфейс, в который будет заворачиваться single page application:
bootstrap(mounter, bus) — вызывается после загрузки сервиса, скажет в какой элемент дома нужно монтироваться, даст ему шину сообщений на которую микросервис у себя подпишется и сможет слушай и посылать запросы и команду
mount() — монтировать приложение из дома
unmount() — демонтаж приложения
unload() — выгрузка приложения
В коде я локально по месту использования буду еще раз в описывать работу каждого метода.
Run in a Docker container
ROOT Docker containers for several linux flavours are available at ROOT’s official DockerHub.
For example, to try out the latest ROOT release just run docker run -it rootproject/root.
Создаем микро-приложение REACT (react-app)
create-single-spa? Directory for new project react-app
? Select type to generate single-spa application / parcel
? Which framework do you want to use? react
? Which package manager do you want to use? npm
? Organization name (use lowercase and dashes) somename Проверим, добавили ли мы карту импорта в нашем root приложении
{
"imports": {
... ,
"react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.development.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.development.js",
"@somename/react-app": "//localhost:8081/somename-projname.js", }
}Готово! Теперь по нашем роуту react-app у нас загружается react микро-проект.
Install via a package manager
Создаем микро-приложение ANGULAR (angular-app)
create-single-spa? Directory for new project angular-app
? Select type to generate single-spa application / parcel
? Which framework do you want to use? angular
? Which package manager do you want to use? npm
? Organization name (use lowercase and dashes) somename Проверим, добавили ли мы карту импорта в нашем root приложении
{ "imports": {
... ,
"@somename/angular-app": "//localhost:8082/main.js", }
}Запускаем, проверяем, все должно работать.
Снова про функцию memset
V597 The compiler could delete the ‘memset’ function call, which is used to flush ‘x’ buffer. The memset_s() function should be used to erase the private data. TMD5.cxx 366
void TMD5::Transform(UInt_t buf[4], const UChar_t in[64])
{ UInt_t a, b, c, d, x[16]; .... // Zero out sensitive information memset(x, 0, sizeof(x));
}Разные предупреждения
V591 Non-void function should return a value. LogLikelihoodFCN.h 108
LogLikelihoodFCN & operator = (const LogLikelihoodFCN & rhs) { SetData(rhs.DataPtr() ); SetModelFunction(rhs.ModelFunctionPtr() ); fNEffPoints = rhs.fNEffPoints; fGrad = rhs.fGrad; fIsExtended = rhs.fIsExtended; fWeight = rhs.fWeight; fExecutionPolicy = rhs.fExecutionPolicy;
}В перегруженном операторе отсутствует возвращаемое значение. Тоже очень распространённая проблема в последнее время.
V596 The object was created but it is not being used. The ‘throw’ keyword could be missing: throw runtime_error(FOO); RTensor.hxx 363
template <typename Value_t, typename Container_t>
inline RTensor<Value_t, Container_t> RTensor<Value_t, Container_t>::Transpose()
{ if (fLayout == MemoryLayout::RowMajor) { fLayout = MemoryLayout::ColumnMajor; } else if (fLayout == MemoryLayout::ColumnMajor) { fLayout = MemoryLayout::RowMajor; } else { std::runtime_error("Memory layout is not known."); } ....
}Ошибка заключается в том, что случайно забыто ключевое слово throw. В результате данный код не генерирует исключение в случае ошибочной ситуации.
Всего нашлось два таких места. Второе:
- V596 The object was created but it is not being used. The ‘throw’ keyword could be missing: throw runtime_error(FOO); Forest.hxx 137
const char *TGHtml::GetPctWidth(TGHtmlElement *p, char *opt, char *ret)
{ int n, m, val; .... if (n < 0 || n > 100) return z; if (opt[0] == 'h') { val = fCanvas->GetHeight() * 100; } else { val = fCanvas->GetWidth() * 100; } if (!fInTd) { snprintf(ret, 15, "%d", val / n); // <= } else { .... } ....
}Случай, похожий на те, что были описаны ранее про массивы. Здесь переменная n ограничивается диапазоном значений от 0 до 100. В таком случае существует ветвь кода, в которой произойдет деление на переменную n со значением 0. Скорее всего, ограничение значения n следует переписать таким образом:
if (n <= 0 || n > 100) return z;V646 Consider inspecting the application’s logic. It’s possible that ‘else’ keyword is missing. TProofServ.cxx 729
TProofServ::TProofServ(Int_t *argc, char **argv, FILE *flog) : TApplication("proofserv", argc, argv, 0, -1)
{ .... if (!logmx.IsDigit()) { if (logmx.EndsWith("K")) { xf = 1024; logmx.Remove(TString::kTrailing, 'K'); } else if (logmx.EndsWith("M")) { xf = 1024*1024; logmx.Remove(TString::kTrailing, 'M'); } if (logmx.EndsWith("G")) { xf = 1024*1024*1024; logmx.Remove(TString::kTrailing, 'G'); } } ....
}Анализатор обнаружил странное форматирование. В одном месте отсутствует ключевое слово else. По коду можно предположить, что код действительно стоит исправить.
И ещё пару мест исправить заодно:
- V646 Consider inspecting the application’s logic. It’s possible that ‘else’ keyword is missing. TFormula_v5.cxx 3702
- V646 Consider inspecting the application’s logic. It’s possible that ‘else’ keyword is missing. RooAbsCategory.cxx 604
V663 Infinite loop is possible. The ‘cin.eof()’ condition is insufficient to break from the loop. Consider adding the ‘cin.fail()’ function call to the conditional expression. MethodKNN.cxx 602
void TMVA::MethodKNN::ReadWeightsFromStream(std::istream& is)
{ .... while (!is.eof()) { std::string line; std::getline(is, line); if (line.empty() || line.find("#") != std::string::npos) { continue; } .... } ....
}При работе с классом std::istream недостаточно вызова функции eof() для завершения цикла. В случае возникновения сбоя при чтении данных, вызов функции eof() будет всегда возвращать значение false, а других точек выхода из цикла в этом коде нет. Для завершения цикла в этом случае необходима дополнительная проверка значения, возвращаемого функцией fail():
while (!is.eof() && !is.fail())
{
....
}Или просто написать:
while (is)
{
....
}V678 An object is used as an argument to its own method. Consider checking the first actual argument of the ‘Copy’ function. TFormLeafInfo.cxx 2414
TFormLeafInfoMultiVarDim::TFormLeafInfoMultiVarDim( const TFormLeafInfoMultiVarDim& orig) : TFormLeafInfo(orig)
{ fNsize = orig.fNsize; fSizes.Copy(fSizes); // <= fCounter2 = orig.fCounter2?orig.fCounter2->DeepCopy():0; fSumOfSizes = orig.fSumOfSizes; fDim = orig.fDim; fVirtDim = orig.fVirtDim; fPrimaryIndex = orig.fPrimaryIndex; fSecondaryIndex = orig.fSecondaryIndex;
}Напоследок вот такая опечаточка. Вместо fSizes надо было передать orig.fSizes в функцию Copy.
Оглавление
- Общая часть
- Зачем это нужно
- Создание root контейнера (определение см. ниже) из вашего монолита
- Создаем микро-приложение VUE (vue-app)
- Создаем микро-приложение REACT (react-app)
- Создаем микро-приложение ANGULAR (angular-app)
Ошибки при работе с массивами
size_t find_last_non_alnum(const std::string &str, std::string::size_type index = std::string::npos) { .... char tmp = Line.GetText()[Cursor]; Line[Cursor] = Line[Cursor - 1]; Line[Cursor] = tmp; ....
}V557 Array overrun is possible. The ‘ivar’ index is pointing beyond array bound. BasicMinimizer.cxx 130
bool BasicMinimizer::SetVariableValue(unsigned int ivar, double val) { if (ivar > fValues.size() ) return false; fValues[ivar] = val; return true;
}Так ошибаться в проверке индекса массива — просто массовая проблема в последнее время. Чуть ли не в каждом третьем проекте встречается. Если при индексации массива в цикле всё просто — почти всегда используется оператор ‘<‘ для сравнения индекса с размером массива, то при такой проверке, как здесь, надо использовать оператор ‘>=’, а не ‘>’. Иначе возможен выход за границу массива на один элемент.
Эту ошибку расплодили по файлу несколько раз:
- V557 Array overrun is possible. The ‘ivar’ index is pointing beyond array bound. BasicMinimizer.cxx 186
- V557 Array overrun is possible. The ‘ivar’ index is pointing beyond array bound. BasicMinimizer.cxx 194
- V557 Array overrun is possible. The ‘ivar’ index is pointing beyond array bound. BasicMinimizer.cxx 209
- V557 Array overrun is possible. The ‘ivar’ index is pointing beyond array bound. BasicMinimizer.cxx 215
- V557 Array overrun is possible. The ‘ivar’ index is pointing beyond array bound. BasicMinimizer.cxx 230
V621 Consider inspecting the ‘for’ operator. It’s possible that the loop will be executed incorrectly or won’t be executed at all. TDataMember.cxx 554
Int_t TDataMember::GetArrayDim() const
{ if (fArrayDim<0 && fInfo) { R__LOCKGUARD(gInterpreterMutex); TDataMember *dm = const_cast<TDataMember*>(this); dm->fArrayDim = gCling->DataMemberInfo_ArrayDim(fInfo); // fArrayMaxIndex should be zero if (dm->fArrayDim) { dm->fArrayMaxIndex = new Int_t[fArrayDim]; for(Int_t dim = 0; dim < fArrayDim; ++dim) { dm->fArrayMaxIndex[dim] = gCling->DataMemberInfo_MaxIndex(fInfo,dim); } } } return fArrayDim;
}Скорее всего, в цикле for хотели сравнивать переменную dim с dm->fArrayDim, а не fArrayDim. Значение используемой переменной — отрицательное, благодаря условию в начале функции. Такой цикл никогда не выполняется.
V767 Suspicious access to element of ‘current’ array by a constant index inside a loop. TClingUtils.cxx 3082
llvm::StringRef ROOT::TMetaUtils::DataMemberInfo__ValidArrayIndex(....)
{ .... while (current!=0) { // Check the token if (isdigit(current[0])) { for(i=0;i<strlen(current);i++) { if (!isdigit(current[0])) { if (errstr) *errstr = current; if (errnum) *errnum = NOT_INT; return llvm::StringRef(); } } } else { // current token is not a digit .... } .... } ....
}
Run on CERN LXPLUS
Note that certain features (e.g. multi-threading capabilities) are not available on lxplus.cern.ch (or, equivalently, lxplus7.cern.ch) due to incompatible versions of certain ROOT dependencies on CentOS7. You can use lxplus8.cern.ch to get access to CentOS8, where this limitation is not present.
Linux package managers
Fedora
Fedora’s ROOT package can be installed with
dnf root python3-root root-notebookCentOS
ROOT is available on CentOS via EPEL. To install ROOT on CentOS, just run
yum epel-release
yum rootArch Linux
Arch’s ROOT package can be installed with
The Arch package uses C++17.
Gentoo
The Gentoo package for ROOT is sci-physics/root.
It can be installed with
emerge sci-physics/rootNixOS/Nix/Nixpkgs
nix-env rootRunning in a temporary environment can be achieved with
nix-shell root rootA root5 package is provided for the legacy software support.
If you encounter any issues, feel free report them to the nixpkgs issue tracker.
Ubuntu and Debian-based distributions
The ROOT team is working on the release of an official .deb package. More news on this topic very soon.
In the meanwhile, ROOT is available on Ubuntu via conda or our pre-compiled binaries.
Slackware
sqg root rootqueue # Create a queue for ROOT with dependenciessbopkg rootqueue # Install ROOT with it's dependenciesRemember, that the official recommendation for all slackbuilds (including ROOT) is that you
chose the FULL INSTALL for Slackware.
Standalone ROOT
If your platform mounts CVMFS
ROOT is directly available via LCG releases.
ROOT installations with minimal external dependencies are available for Fedora, Ubuntu, CentOS7 and MacOS at:
/cvmfs/sft.cern.ch/lcg/app/releases/ROOT/<version>/<platform>For example, to set up ROOT 6.28/04 on a Centos8 machine, just run:
source /cvmfs/sft.cern.ch/lcg/app/releases/ROOT/6.28.04/x86_64-centos8-gcc8.5-opt/bin/thisroot.shMake sure you use your system’s default compiler, just like this ROOT build.
MacOS package managers
Homebrew
On Mac, ROOT is also available as a homebrew formula.
You can install it with
Macports
After installing macports, the ROOT port can be installed with
port root6Nix/Nixpkgs
The same instructions as for Linux apply when running on macOS.
Дебют новой диагностики
V1046 Unsafe usage of the bool’ and ‘int’ types together in the operation ‘&=’. GSLMultiRootFinder.h 175
int AddFunction(const ROOT::Math::IMultiGenFunction & func) { ROOT::Math::IMultiGenFunction * f = func.Clone(); if (!f) return 0; fFunctions.push_back(f); return fFunctions.size();
}
template<class FuncIterator>
bool SetFunctionList( FuncIterator begin, FuncIterator end) { bool ret = true; for (FuncIterator itr = begin; itr != end; ++itr) { const ROOT::Math::IMultiGenFunction * f = *itr; ret &= AddFunction(*f); } return ret;
}Бета-версия анализатора, которая использовалась при проверке, нашла вот такую потрясающую ошибку.
Ожидание. Функция SetFunctionList обходит список итераторов. Если хоть один из них будет невалидным, то возвращаемое значение будет false, иначе true.
Реальность. Функция SetFunctionList может возвращать значение false даже для валидных итераторов. Разберёмся в ситуации.Функция AddFunction возвращает количество валидных итераторов в списке fFunctions. Т.е. при добавлении ненулевых итераторов, размер этого списка будет последовательно увеличиваться: 1, 2, 3, 4 и т.д. Вот тут и начнёт проявлять себя ошибка в коде:
ret &= AddFunction(*f);Т.к. функция возвращает результат типа int, а не bool, то операция ‘&=’ с чётными числами будет давать значение false. Ведь младший бит чётных чисел всегда будет равен нулю. Следовательно, такая неочевидная ошибка будет портить результат функции SetFunctionsList даже для валидных аргументов.

Conda
For any Linux distribution and MacOS, ROOT is available as a conda package. To create a new conda environment containing ROOT and activate it, execute
conda config channel_priority strict
conda create conda-forge <my-environment> root
conda activate <my-environment>The conda package uses C++17.
More instructions about using this package are available in this blog post.
Please report any issues with the conda package here.
Зачем это нужно
Существует 2 типа архитектуры:
- Монолит
- Микросервисная архитектура

С монолитом все довольно просто и максимально всем нам знакомо. Сильная связанность, огромные блоки кода, общий репозиторий, куча методов.
На старте монолитная архитектура максимально удобна и быстра. Нет никаких проблем и сложностей в создании каких либо интеграционных файлов, прослоек, событийных моделей, шин данных и тд.
Проблема появляется когда разрастается ваш проект, появляется много отдельного, сложного функционала разного назначения. Весь этот функционал начинает завязываться внутри проекта на какие то общие модели, состояния, утилиты, интерфейсы, методы и тп.
Так же количество директорий и файлов в проекте со временем становится огромное, появляются проблемы поиска и понимания проекта в целом, теряется «взгляд сверху», который придает ясность того, чем мы занимаемся, где что лежит и кому это нужно.
В придачу ко всему этому срабатывает закон Иглсона, который говорит, что Ваш код, который вы не просматривали 6 или более месяцев, выглядит так, будто его написал кто-то другой.
Самое больное, все будет обрастать в геометрической прогрессии, в результате начнется костыли, которые необходимо добавить из за опять же сложности поддержания кода в связи с вышеописанным и со временем встречающихся волн невменяемых сроков.
В результате, если у вас живой проект, который постоянно развивается, это станет большой проблемой, вечным недовольством вашей команды, огромное количество человека — часов на внесения несущественных изменений в проект, низкий порог входа для новых сотрудников и много времени на выкатывание проекта на бой. Это все приводит к беспорядку, ну мы же любим порядок?
Всегда ли так происходит с монолитом?
Конечно же нет! Все зависит от типа вашего проекта, от тех проблем, которые возникают при командной разработке. Ваш проект может быть не такой большой, выполнять какую то одну сложную бизнес задачу, это нормально и полагаю — правильно.
В первую очередь нам необходимо обратить внимание на параметры нашего проекта.
Попробую вынести пункты, по которым можно понять, так ли нам необходима микросервисная архитектура:
- Над проектом работает 2 и более команды, количество фронтенд разработчиков 10+;
- Ваш проект состоит из 2 и более бизнес модели, например у вас интернет магазин с огромным количеством товаров, фильтров, нотификации, и функционал курьерского распределения доставок (2 отдельные не маленькие бизнес модели, которые будут друг другу мешать). Это все может жить отдельно и не зависеть друг от друга.
- Набор возможностей UI растёт ежедневно или еженедельно, не оказывая влияния на остальную часть системы.
Микрофронтенды применяются для того, чтобы:
- Отдельные части фронтенда могли разрабатываться, тестироваться и развёртываться независимо;
- Отдельные части фронтенда могли быть добавлены, удалены или заменены без повторной сборки;
- Разные части фронтенда могли быть созданы с помощью разных технологий.
- В идеале, связи между этими микро-приложениями не должно быть от слова «совсем», поэтому этот пункт должен тоже влиять на принятии решения о выпиливании бизнес-кейсов (или кейсы для расширенного пользовательского опыта) в отдельный микро-сервис.
- Скорость разработки должна быть постоянной, несмотря на рост приложения
- Разные команды должны иметь возможность использовать собственные инструменты.
Какие бонусы мы еще можем получить от single-spa библиотеки?
- Вы можете управлять большими общими зависимостями (например, библиотеками React, Vue или Angular) проще с помощью карты импорта, как вы увидите позже в этой должности.
- Single-spa имеет ленивую загрузку включен для модулей в браузере, так что ваше приложение будет загружать модули только тогда, когда это необходимо.
- Разделение приложения на несколько модулей в браузере позволяет разрабатывать и развертывать приложение независимо друг от друга.
Микросервис в моем понимании — самостоятельный single page application, который будет решать только одну задачу пользователя. Это приложение так же не должно решать задачу команды целиком.
SystemJS — это библиотека JS с открытым исходным кодом, которая обычно используется в качестве полифилла для браузеров.
Полифилл является частью кода JS, используемого для обеспечения современной функциональности для старых браузеров, которые не поддерживают его.
Одной из особенностей SystemJS является карта импорта, которая позволяет импортировать модуль по сети и сопоставить его с переменным именем.
Например, можно использовать карту импорта для библиотеки React, которая загружается через CDN:
Если вы создаете проект с нуля, даже с учетом того, что вы определили все параметры вашего проекта, решили что у вас будет огромный Мега супер проект с командой 30+ человек, постойте!
Мне очень нравится мысль небезызвестного основоположника идеи микросервисов — Мартин Фаулер.
Он предложил объединить монолитный подход и микросервисы в один (MonolithFirst). Основная его идея звучит так:
не следует начинать новый проект с микросервисов даже при полной уверенности, что будущее приложение будет достаточно большим, чтобы оправдать такой подход
Так же здесь опишу минусы использования такой архитектуры:
- Взаимодействие между фрагментами невозможно обеспечить стандартными ламповыми методами (DI, например).
- Как быть с общими зависимостями? Ведь размер приложения будет расти как на дрожжах, если их не выносить из фрагментов.
- За роутинг в конечном приложении все равно должен отвечать кто-то один.
- Неясно, что делать с тем, что разные микросервисы могут находиться на разных доменах
- Что делать, если один из фрагментов недоступен / не может отрисоваться.
Создаем микро-приложение VUE (vue-app)
Во-первых, нам нужно установить глобально create-single-spa, интерфейс командной строки, который поможет нам создавать новые проекты single-spa с помощью простых команд.
Заходим в консоль
npm install --global create-single-spaСоздаем простое приложение vue с помощью команды в консоле
create-single-spaИнтерфей командной строки предложит выбрать директорию, название проекта, организации и тип приложения для создания

? Directory for new project vue-app
? Select type to generate single-spa application / parcel
? Which framework do you want to use? vue
? Which package manager do you want to use? npm
? Organization name (use lowercase and dashes) somename Запускаем наше микро-приложение
npm i
npm run serve --port 8000 Когда мы введем путь в браузере localhost:8080/, в случае с vue мы увидим пустой экран. Что же произошло?
Так как в созданном микро-приложении нет файла index.js.
Single-spa предоставляет игровую площадку, с которой можно загружать приложение через интернет, поэтому давайте сначала воспользуемся ей.
Добавим в index.js
single-spa-playground.org/playground/instant-test?name=@some-name/vue-app&url=8000
При создании root приложения, мы заранее добавили карту для загрузки нашего vue проекта.
{
"imports": {
... ,
"vue": "https://unpkg.com/vue",
"vue-router": "https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js",
"@somename/vue-app": "//localhost:8080/js/app.js"
}
}Готова! Теперь с нашего angular root проекта мы можем загружать микро-приложения, написанное на vue.
Download a pre-compiled binary distribution
We distribute pre-compiled ROOT for several major Linux distributions as well as MacOS and (as a beta) Windows.
The steps to install a pre-compiled binary are simple:
- Install all required dependencies with the system package manager
- Download the release for the desired platform and ROOT version
- Unpack the archive
- Add the ROOT libraries and executables to your environment by sourcing the appropriate
thisroot.*script. These setup scripts can be found in the ROOT binary release, in thebindirectory.
wget https://root.cern/download/root_v6.28.04.Linux-centos8-x86_64-gcc8.5.tar.gz root_v6.28.04.Linux-centos8-x86_64-gcc8.5.tar.gz
root/bin/thisroot.sh To avoid having to source thisroot.sh every time one needs to use ROOT, it is typical to add the command to
.bashrc, .profile or analogous configuration files.
Note, however, that sourcing thisroot.sh might interfere with ROOT versions installed with different methods.
.11.3
.bat :
:\Program \Microsoft \2019\Community>cd
:\Users\username>c:\root\bin\thisroot.bat
:\Users\username>root .28/04 ://root.cern : . . :34:39 /6@6 .29.30133.0 / Gentoo Prefix on CVMFS
ROOT is also experimentally available in a Gentoo Prefix installation
inside the contrib area of the SFT CVMFS repository. To use it from there, run
$ /cvmfs/sft.cern.ch/lcg/contrib/gentoo/linux/x86_64/startprefixThis will drop you into a new shell where all software from the prefix is available.
Ошибки в условных выражениях
V501 There are identical sub-expressions to the left and to the right of the ‘&&’ operator: module && module rootcling_impl.cxx 3650
virtual void HandleDiagnostic(....) override
{ .... bool isROOTSystemModuleDiag = module && ....; bool isSystemModuleDiag = module && module && module->IsSystem; if (!isROOTSystemModuleDiag && !isSystemModuleDiag) fChild->HandleDiagnostic(DiagLevel, Info); ....
}Начнём с самого безобидного примера. Указатель module проверяется два раза. Скорее всего, одна проверка лишняя. Но код лучше исправить, чтобы не возникало лишних вопросов.
TAuthenticate::TAuthenticate(TSocket *sock, const char *remote, const char *proto, const char *user)
{ .... // If generic THostAuth (i.e. with wild card or user == any) // make a personalized memory copy of this THostAuth if (strchr(fHostAuth->GetHost(),'*') || strchr(fHostAuth->GetHost(),'*') || fHostAuth->GetServer() == -1 ) { fHostAuth = new THostAuth(*fHostAuth); fHostAuth->SetHost(fqdn); fHostAuth->SetUser(checkUser); fHostAuth->SetServer(servtype); } ....
}Тут в строке fHostAuth->GetHost() ищется один и тот же символ — ‘*’. Возможно, одним из них должен быть символ ‘?’. Обычно их используют для задания разных масок.
Int_t TProofMonSenderML::SendSummary(TList *recs, const char *id)
{ .... if (fSummaryVrs == 0) { if ((dsn = recs->FindObject("dataset"))) recs->Remove(dsn); } else if (fSummaryVrs == 0) { // Only the first records xrecs = new TList; xrecs->SetOwner(kFALSE); TIter nxr(recs); TObject *o = 0; while ((o = nxr())) { if (!strcmp(o->GetName(), "vmemmxw")) break; xrecs->Add(o); } } ....
}V523 The ‘then’ statement is equivalent to the ‘else’ statement. TKDTree.cxx 805
template <typename Index, typename Value>
void TKDTree<Index, Value>::UpdateRange(....)
{ .... if (point[fAxis[inode]]<=fValue[inode]){ //first examine the node that contains the point UpdateRange(GetLeft(inode),point, range, res); UpdateRange(GetRight(inode),point, range, res); } else { UpdateRange(GetLeft(inode),point, range, res); UpdateRange(GetRight(inode),point, range, res); } ....
}Один и тот же copy-paste-код выполняется при любом условии. Возможно, есть опечатка в словах left и right.
Подобного подозрительного кода в проекте немало:
- V523 The ‘then’ statement is equivalent to the ‘else’ statement. TContainerConverters.cxx 51
- V523 The ‘then’ statement is equivalent to the ‘else’ statement. TWebFile.cxx 1310
- V523 The ‘then’ statement is equivalent to the ‘else’ statement. MethodMLP.cxx 423
- V523 The ‘then’ statement is equivalent to the ‘else’ statement. RooAbsCategory.cxx 394
V547 Expression ‘!file_name_value.empty()’ is always false. SelectionRules.cxx 1423
bool SelectionRules::AreAllSelectionRulesUsed() const { for(auto&& rule : fClassSelectionRules){ .... std::string file_name_value; if (!rule.GetAttributeValue("file_name", file_name_value)) file_name_value.clear(); if (!file_name_value.empty()) { // <= // don't complain about defined_in rules continue; } const char* attrName = nullptr; const char* attrVal = nullptr; if (!file_name_value.empty()) { // <= attrName = "file name"; attrVal = file_name_value.c_str(); } else { attrName = "class"; if (!name.empty()) attrVal = name.c_str(); } ROOT::TMetaUtils::Warning(0,"Unused %s rule: %s\n", attrName, attrVal); } ....
}Скорее всего, тут нет ошибки. Анализатор обнаружил код, который можно сократить. Т.к. значение file_name_value.empty() проверяется в начале цикла, то ниже по коду эту проверку можно убрать, заметно сократив количество ненужного кода.
TString TTabCom::DetermineClass(const char varName[])
{ .... c = file1.get(); if (!file1 || c <= 0 || c == '*' || c != '(') { Error("TTabCom::DetermineClass", "variable \"%s\" not defined?", varName); goto cleanup; } ....
}Рассмотрим сокращённую часть условного выражения, на которое указал анализатор:
if (.... || c == '*' || c != '(') { ....
}Условие не зависит от того, равен символ «звёздочке» или нет. Эта часть условного выражения всегда будет истинна для любого символа, отличного от ‘(‘. В этом легко убедиться, если построить таблицу истинности.
Ещё два места со странной логикой в условных выражениях:
- V590 Consider inspecting this expression. The expression is excessive or contains a misprint. TFile.cxx 3963
- V590 Consider inspecting this expression. The expression is excessive or contains a misprint. TStreamerInfoActions.cxx 3084
Int_t TProofServ::HandleSocketInput(TMessage *mess, Bool_t all)
{ .... if (Int_t ret = fProof->AddWorkers(workerList) < 0) { Error("HandleSocketInput:kPROOF_GETSLAVEINFO", "adding a list of worker nodes returned: %d", ret); } ....
}Ошибка, которую обнаружил анализатор, проявляет себя только при некорректной работе программы. Переменная ret должна хранить код возврата функции AddWorkers и в случае нештатной ситуации выводить это значение в лог. Но код работает не так. В условии не хватает дополнительных скобок, задающих приоритет операций. В переменную ret сохраняется не код возврата, а результат логического сравнения, т.е. только 0 или 1.
Есть ещё одно место с похожим дефектом:
- V593 Consider reviewing the expression of the ‘A = B < C’ kind. The expression is calculated as following: ‘A = (B < C)’. TProofServ.cxx 3897
V768 The enumeration constant ‘kCostComplexityPruning’ is used as a variable of a Boolean-type. MethodDT.cxx 283
enum EPruneMethod {kExpectedErrorPruning=0, kCostComplexityPruning, kNoPruning};
void TMVA::MethodDT::ProcessOptions()
{ .... if (fPruneStrength < 0) fAutomatic = kTRUE; else fAutomatic = kFALSE; if (fAutomatic && fPruneMethod==!DecisionTree::kCostComplexityPruning){ Log() << kFATAL << "Sorry automatic pruning strength determination is ...." << Endl; } ....
}Snap
On many Linux distributions, ROOT can be installed via Snap. For example, on Ubuntu:
snap root-framework
snap run root-framework# or if there is no fear of conflicts with other installations:root # and the output of `which root` should contain `/snap`To use ROOT from Python, the Snap package bundles its own Python 3.8 interpreter
that knows where to find the ROOT libraries. This is done to avoid interference
with other system packages. You should use pyroot rather than python to make
use of the PyROOT features with the Snap package:
Утечка памяти
V773 The function was exited without releasing the ‘optionlist’ pointer. A memory leak is possible. TDataMember.cxx 355
void TDataMember::Init(bool afterReading)
{ .... TList *optionlist = new TList(); //storage for options strings for (i=0;i<token_cnt;i++) { if (strstr(tokens[i],"Items")) { ptr1 = R__STRTOK_R(tokens[i], "()", &rest); if (ptr1 == 0) { Fatal("TDataMember","Internal error, found \"Items....",GetTitle()); return; } ptr1 = R__STRTOK_R(nullptr, "()", &rest); if (ptr1 == 0) { Fatal("TDataMember","Internal error, found \"Items....",GetTitle()); return; } .... } .... } .... // dispose of temporary option list... delete optionlist; ....
}Во время выхода из функции не предусмотрено освобождение памяти по указателю optionList. Нужно ли это в данном конкретном случае — мне сложно сказать. Но обычно такие ошибки исправляют в проектах по отчётам PVS-Studio. Всё зависит от того, должна ли программа пытаться продолжить работать в нештатной ситуации или нет. Таких предупреждений в проекте много, разработчикам лучше перепроверить проект самостоятельно и посмотреть полный отчёт анализатора.
Создание root контейнера
Заходим в консоль
ng add single-spa-angular
npm i systemjs@6.1.4,
npm i -d @types/systemjs@6.1.0,
npm import-map-overrides@1.8.0В ts.config.app.json глобально импортируем декларации (типы)
// ts.config.app.json
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [
(+) "systemjs"
]
},Добавляем в app-routing.module.ts все микроприложения, которые мы добавим в root
// app-routing.module.ts
{
path: 'vue-app',
children: [
{
path: '**',
loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
data: { app: '@somename/vue-app' }
}
]
},
{
path: 'angular-app',
children: [
{
path: '**',
loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
data: { app: '@somename/angular-app' }
}
]
},
{
path: 'react-app',
children: [
{
path: '**',
loadChildren: ( ) => import('./spa-host/spa-host.module').then(m => m.SpaHostModule),
data: { app: '@somename/react-app' }
}
]
},Так же нужно добавить config
// extra-webpack.config.json
module.exports = (angularWebpackConfig, options) => {
return {
...angularWebpackConfig,
module: {
...angularWebpackConfig.module,
rules: [
...angularWebpackConfig.module.rules,
{
parser: {
system: false
}
}
]
}
};
}Изменим файл package.json, добавим в него все необходимые для работы либы
// package.json
"dependencies": {
...,
(+) "single-spa": "^5.4.2",
(+) "single-spa-angular": "^4.2.0",
(+) "import-map-overrides": "^1.8.0",
(+) "systemjs": "^6.1.4",
}
"devDependencies": {
...,
(+) "@angular-builders/custom-webpack": "^9",
(+) "@types/systemjs": "^6.1.0",
}Добавляем необходимые библиотеки в angular.json
// angular.json
{
...,
"architect": {
"build": {
...,
"scripts": [
...,
(+) "node_modules/systemjs/dist/system.min.js",
(+) "node_modules/systemjs/dist/extras/amd.min.js",
(+) "node_modules/systemjs/dist/extras/named-exports.min.js",
(+) "node_modules/systemjs/dist/extras/named-register.min.js",
(+) "node_modules/import-map-overrides/dist/import-map-overrides.js"
]
}
}
},В корне проекта создаем папку single-spa. В него добавим 2 файла.
1. route-reuse-strategy.ts — файл маршрутизации наших микросервисов.
Если дочернее приложение выполняет маршрутизацию внутри себя, это приложение интерпретирует это как изменение маршрута.
По умолчанию это приведет к уничтожению текущего компонента и замене его новым экземпляром того же компонента spa-host.
Эта стратегия повторного использования маршрута смотрит на routeData.app, чтобы определить, должен ли новый маршрут быть обрабатывается как тот же маршрут, что и предыдущий, гарантируя, что мы не перемонтируем дочернее приложение, когда указанное дочернее приложение маршруты внутри себя.
// route-reuse-strategy.ts
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
import { Injectable } from '@angular/core';
@Injectable()
export class MicroFrontendRouteReuseStrategy extends RouteReuseStrategy {
shouldDetach(): boolean {
// маршрут не сохраняется
return false;
}
store(): void { }
shouldAttach(): boolean {
return false;
}
// время присоединения маршрута
retrieve(): DetachedRouteHandle {
return null;
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig || (future.data.app && (future.data.app === curr.data.app));
}
}2. Сервис single-spa.service.ts
В сервисе будет храниться метод монтирования (mount) и демонтирования (unmount) микро-фронтенд приложений.
mount — функция жизненного цикла, которая будет вызываться всякий раз, когда зарегистрированное приложение не смонтировано, но его функция активности возвращает true. При вызове эта функция должна просмотреть URL-адрес, чтобы определить активный маршрут, а затем создать элементы DOM, события DOM и т.п.
unmount — функция жизненного цикла, которая будет вызываться всякий раз, когда монтируется зарегистрированное приложение, но ее функция активности возвращает false. При вызове эта функция должна очищать все элементы DOM.
//single-spa.service.ts
import { Injectable } from '@angular/core';
import { mountRootParcel, Parcel, ParcelConfig } from 'single-spa';
import { Observable, from, of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class SingleSpaService {
private loadedParcels: {
[appName: string]: Parcel;
} = {};
mount(appName: string, domElement: HTMLElement): Observable<unknown> {
return from(System.import<ParcelConfig>(appName)).pipe(
tap((app: ParcelConfig) => {
this.loadedParcels[appName] = mountRootParcel(app, {
domElement
});
})
);
}
unmount(appName: string): Observable<unknown> {
return from(this.loadedParcels[appName].unmount()).pipe(
tap(( ) => delete this.loadedParcels[appName])
);
}
}Далее создаем директорию container/app/spa-host.
Этот модуль будет реализовывать регистрации и отображение наших микро-фронтенд приложений в root.
Добавим в модуль 3 файла.
1. Сам модуль spa-host.module.ts
//spa-host.module.ts
import { RouterModule, Routes } from '@angular/router';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { SpaUnmountGuard } from './spa-unmount.guard';
import { SpaHostComponent } from './spa-host.component';
const routes: Routes = [
{
path: '',
canDeactivate: [SpaUnmountGuard],
component: SpaHostComponent,
},
];
@NgModule({
declarations: [SpaHostComponent],
imports: [CommonModule, RouterModule.forChild(routes)]
})
export class SpaHostModule {}2. Компонент spa-host.component.ts — координирует монтаж и демонтаж микро-фронтенд приложений
// spa-host.component.ts
import { Component, OnInit, ViewChild, ElementRef, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import {SingleSpaService} from '../../single-spa/single-spa.service';
@Component({
selector: 'app-spa-host',
template: '<div #appContainer></div>',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpaHostComponent implements OnInit {
@ViewChild('appContainer', { static: true })
appContainerRef: ElementRef;
appName: string;
constructor(private singleSpaService: SingleSpaService, private route: ActivatedRoute) { }
ngOnInit() {
// тащим название подгружаемой карты
this.appName = this.route.snapshot.data.app;
this.mount().subscribe();
}
// собираем наш подгруженный проект по выбранному роуту
mount(): Observable<unknown> {
return this.singleSpaService.mount(this.appName, this.appContainerRef.nativeElement);
}
// разбираем
unmount(): Observable<unknown> {
return this.singleSpaService.unmount(this.appName);
}
}3. spa-unmount.guard.ts — проверяет, если имя приложения в роуте другое, разбираем предыдущий сервис, если тоже, просто переходим на него.
// spa-unmount.guard.ts
import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { SpaHostComponent } from './spa-host.component';
@Injectable({ providedIn: 'root' })
export class SpaUnmountGuard implements CanDeactivate<SpaHostComponent> {
canDeactivate(
component: SpaHostComponent,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState: RouterStateSnapshot
): boolean | Observable<boolean> {
const currentApp = component.appName;
const nextApp = this.extractAppDataFromRouteTree(nextState.root);
if (currentApp === nextApp) {
return true;
}
return component.unmount().pipe(map(_ => true));
}
private extractAppDataFromRouteTree(routeFragment: ActivatedRouteSnapshot): string {
if (routeFragment.data && routeFragment.data.app) {
return routeFragment.data.app;
}
if (!routeFragment.children.length) {
return null;
}
return routeFragment.children.map(r => this.extractAppDataFromRouteTree(r)).find(r => r !== null);
}
}Регистрируем все что добавили в в app.module
// app.module.ts
providers: [
...,
{
(+) provide: RouteReuseStrategy,
(+) useClass: MicroFrontendRouteReuseStrategy
}
]// main.ts
import { enableProdMode, NgZone } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { start as singleSpaStart } from 'single-spa';
import { getSingleSpaExtraProviders } from 'single-spa-angular';
import { AppModule } from './app/app.module';
import { PlatformLocation } from '@angular/common';
if (environment.production) {
enableProdMode();
}
singleSpaStart();
// название проекта
const appId = 'container-app';
// Так как наше приложение использует маршрутизацию, мне необходимо импортировать функцию getSingleSpaExtraProviders.
platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule).then(module => {
NgZone.isInAngularZone = () => {
// @ts-ignore
return window.Zone.current._properties[appId] === true;
};
const rootPlatformLocation = module.injector.get(PlatformLocation) as any;
const rootZone = module.injector.get(NgZone);
// tslint:disable-next-line:no-string-literal
rootZone['_inner']._properties[appId] = true;
rootPlatformLocation.setNgZone(rootZone);
})
.catch(err => {});<head>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My first microfrontend root project</title>
<base data-hren="/">
...
(+) <meta name="importmap-type" content="systemjs-importmap" />
<script type="systemjs-importmap" src="/assets/import-map.json"></script>
</head>
<body>
<app-root></app-root>
<import-map-overrides-full></import-map-overrides-full>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>Build from source
In case no other installation method is available, or if you want full control over the options ROOT is built with,
it is possible to compile ROOT from source. See Building ROOT from source for detailed instructions.
As a quick summary, after installing all required dependencies, ROOT can be compiled with these commands on most UNIX-like systems:
# The latest stable branch gets updated automatically on each release.# You may update your local copy by issuing a `git pull` command from within `root_src/`.git clone latest-stable 1 https://github.com/root-project/root.git root_src
root_build root_install root_build
cmake ../root_install ../root_src # && check cmake configuration output for warnings or errorscmake # if you have 4 cores available for compilation ../root_install/bin/thisroot.sh And similarly, on Windows, inside a x86 Native Tools Command Prompt for VS 2019, ROOT can be compiled with these commands:
rem The `latest-stable` branch gets updated automatically on each release.rem You may update your local copy by issuing a `git pull` command from within `root_src`.:\Users\username>git ://github.com/root.git
:\Users\username>mkdir
:\Users\username>cmake "Visual Studio 16 2019" _VERBOSE_MAKEFILE _INSTALL_PREFIX../root_install ../root_src
:\Users\username>cmake .
:\Users\username>..\root_install\bin\thisroot.batНекорректный код с указателями
V522 Dereferencing of the null pointer ‘pre’ might take place. TSynapse.cxx 61
void TSynapse::SetPre(TNeuron * pre)
{ if (pre) { Error("SetPre","this synapse is already assigned to a pre-neuron."); return; } fpre = pre; pre->AddPost(this);
}Я попытался разобраться в этом странном коде. Вроде задумка в том, чтобы не выставлять новое значение полю fpre. Тогда тут допустили ошибку, перепутав указатель, который следует проверить в условии. В текущей реализации, если передать значение nullptr в функцию SetPre, то произойдёт разыменование нулевого указателя.
Скорее всего, правильно так:
void TSynapse::SetPre(TNeuron * pre)
{ if (fpre) { Error("SetPre","this synapse is already assigned to a pre-neuron."); return; } fpre = pre; pre->AddPost(this);
}Правда, это всё равно не спасёт функцию от передачи нулевого указателя. Но, по крайней мере, такой код выглядит более логичным, чем первоначальный вариант.
Вот ещё одно место, которое скопировано отсюда с небольшими изменениями:
- V522 Dereferencing of the null pointer ‘post’ might take place. TSynapse.cxx 74
V595 The ‘N’ pointer was utilized before it was verified against nullptr. Check lines: 484, 488. Scanner.cxx 484
bool RScanner::shouldVisitDecl(clang::NamedDecl *D)
{ if (auto M = D->getOwningModule()) { // <= 2 return fInterpreter.getSema().isModuleVisible(M); } return true;
}
bool RScanner::VisitNamespaceDecl(clang::NamespaceDecl* N)
{ if (fScanType == EScanType::kOnePCM) return true; if (!shouldVisitDecl(N)) // <= 1 return true; if((N && N->isImplicit()) || !N){ // <= 3 return true; } ....
}Анализатор обнаружил очень опасный код! Указатель N в первом случае разыменовывается без проверки на нулевое значение. Причём обращение к непроверенному указателю и не разглядишь. Это происходит внутри функции shouldVisitDecl.
Традиционно, это диагностическое правило выдаёт много полезных предупреждений. Вот некоторые из них:
- V595 The ‘file’ pointer was utilized before it was verified against nullptr. Check lines: 141, 153. TFileCacheRead.cxx 141
- V595 The ‘fFree’ pointer was utilized before it was verified against nullptr. Check lines: 2029, 2038. TFile.cxx 2029
- V595 The ‘tbuf’ pointer was utilized before it was verified against nullptr. Check lines: 586, 591. TGText.cxx 586
- V595 The ‘fPlayer’ pointer was utilized before it was verified against nullptr. Check lines: 3425, 3430. TProof.cxx 3425
- V595 The ‘gProofServ’ pointer was utilized before it was verified against nullptr. Check lines: 1192, 1194. TProofPlayer.cxx 1192
- V595 The ‘projDataTmp’ pointer was utilized before it was verified against nullptr. Check lines: 791, 804. RooSimultaneous.cxx 791
Следующий пример не является ошибкой, но в очередной раз демонстрирует, что макросы поощряют написание неправильного или избыточного кода.
V571 Recurring check. The ‘if (fCanvasImp)’ condition was already verified in line 799. TCanvas.cxx 800
#define SafeDelete(p) { if (p) { delete p; p = 0; } }
void TCanvas::Close(Option_t *option)
{ .... if (fCanvasImp) SafeDelete(fCanvasImp); ....
}Указатель fCanvasImp проверяется дважды. Одна из проверок уже реализована в макросе SafeDelete. Одна из проблем с макросами в том, что к ним часто затруднена навигация из кода, поэтому многие не изучают их содержимое перед использованием.
Complete environment
/cvmfs/sft.cern.ch/lcg/views/LCG_<version>/<platform>LCG views are available for CentOS7, CentOS8 and the latest MacOS and Ubuntu releases.
For example, on CERN LXPLUS, you can set up a full environment that contains ROOT 6.28/04 with:
source /cvmfs/sft.cern.ch/lcg/views/LCG_100/x86_64-centos8-gcc8.5-opt/setup.shTo check what ROOT version is contained in an LCG release, you can visit lcginfo.cern.ch.
Заключение
Примерно год назад делался обзор кода проекта NCBI Genome Workbench. Этот проект тоже используется в научных исследованиях, но генома. К чему я веду, программное обеспечение в этой сфере очень важно, но его качеству не уделяют должного внимания.
Кстати, на днях вышла macOS 10.15 Catalina, в которой отказались от поддержки 32-х битных приложений. И в PVS-Studio есть большой набор правил для выявления проблем при портировании программ на 64-х битные системы. Подробнее об этом в посте блога анализатора.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Svyatoslav Razmyslov. Analyzing the Code of ROOT, Scientific Data Analysis Framework.






