Automated check for circular compile time dependencies

I have a case of cyclical compile time dependencies. I have read lot’s about it and watched the great video on youtube.

Updating a View or Controller or many other files causes a 70 module recompile. I have discovered that my extensive (ab)use of controller level plugs is one cause, but there are others, such as an over-imported ViewHelpers module. I am slowly but surely removing imports, which I think is getting me closer to resolving this issue :crossed_fingers:

My question is, from an xref graph or another tool out there, is it possible to ascertain whether there is a cyclical dependency issue ? I am wondering whether I could run a check to my CI to ensure we never get into this position again.


lib/ev2_web/views/production/custom_field_view.ex
├── lib/ev2_web.ex (compile)
├── lib/ev2_web/router.ex
│   ├── lib/ev2_web/controllers/dashboard_controller.ex
│   │   ├── lib/ev2_web.ex (compile)
│   │   ├── lib/ev2_web/controllers/data_plugs.ex (compile)
│   │   │   ├── lib/ev2/accounts/accounts.ex
│   │   │   │   ├── lib/ev2/accounts/core_logic.ex (compile)
│   │   │   │   ├── lib/ev2/accounts/login_attempt/login_attempt_api.ex (compile)
│   │   │   │   │   ├── lib/ev2/accounts/accounts.ex
│   │   │   │   │   ├── lib/ev2/accounts/login_attempt/login_attempt.ex (struct)
│   │   │   │   │   │   └── lib/ev2/accounts/user/user.ex
│   │   │   │   │   │       ├── lib/ev2/accounts/login_attempt/login_attempt.ex
│   │   │   │   │   │       ├── lib/ev2/accounts/role/project_role.ex
│   │   │   │   │   │       │   ├── lib/ev2/accounts/role/role.ex
│   │   │   │   │   │       │   │   └── lib/ev2/accounts/role/permission.ex
│   │   │   │   │   │       │   │       └── lib/ev2/accounts/role/role.ex
│   │   │   │   │   │       │   ├── lib/ev2/accounts/user/user.ex
│   │   │   │   │   │       │   └── lib/ev2/production/department/department.ex
│   │   │   │   │   │       │       └── lib/ev2/production/job_title/job_title.ex
│   │   │   │   │   │       │           ├── lib/ev2/production/contract_type/contract_type.ex
│   │   │   │   │   │       │           │   └── lib/ev2/production/job_title/job_title.ex
│   │   │   │   │   │       │           └── lib/ev2/production/department/department.ex
│   │   │   │   │   │       ├── lib/ev2/accounts/role/role.ex
│   │   │   │   │   │       ├── lib/ev2/accounts/startpack/startpack.ex
│   │   │   │   │   │       │   ├── lib/ev2/accounts/user/user.ex
│   │   │   │   │   │       │   ├── lib/ev2/ecto_types/string/trim.ex (compile)
│   │   │   │   │   │       │   ├── lib/ev2/ecto_types/string/upcase.ex (compile)
│   │   │   │   │   │       │   ├── lib/ev2/lib/doc_security/doc_security.ex
│   │   │   │   │   │       │   │   ├── lib/ev2/lib/doc_security/anti_virus/anti_virus.ex (compile)
│   │   │   │   │   │       │   │   └── lib/ev2/lib/doc_security/file_type/file_type.ex (compile)
│   │   │   │   │   │       │   ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   ├── lib/ev2/lib/doc_security/doc_security.ex
│   │   │   │   │   │       │   │   ├── lib/ev2/lib/utils.ex
│   │   │   │   │   │       │   │   ├── lib/ev2/lib/utils/time_utils.ex
│   │   │   │   │   │       │   │   ├── lib/ev2/timecards/tc_utils.ex
│   │   │   │   │   │       │   │   │   ├── lib/ev2/lib/utils/time_utils.ex
│   │   │   │   │   │       │   │   │   ├── lib/ev2/production/prod_utils.ex
│   │   │   │   │   │       │   │   │   ├── lib/ev2/timecards/approver/approver.ex (struct)
│   │   │   │   │   │       │   │   │   ├── lib/ev2/timecards/timecard/timecard.ex (struct)
│   │   │   │   │   │       │   │   │   │   ├── lib/ev2/lib/utils/time_utils.ex
│   │   │   │   │   │       │   │   │   │   ├── lib/ev2/timecards/approval/approval.ex
│   │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/timecards/approver/approver.ex
│   │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/timecards/timecard/timecard.ex
│   │   │   │   │   │       │   │   │   │   │   └── lib/ev2/timecards/timecard_data/timecard_data.ex
│   │   │   │   │   │       │   │   │   │   │       ├── lib/ev2/ecto_types/string/trim.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   │   │   │       ├── lib/ev2/production/production.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/altered_document/altered_document_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/altered_document/altered_document.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   ├── lib/ev2/production/document/document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   ├── lib/ev2/production/altered_document/altered_document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   ├── lib/ev2/production/offer/offer.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/accounts/accounts.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/accounts/role/role_api.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   ├── lib/ev2/accounts/role/helpers.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   │   ├── lib/ev2/accounts/role/project_role.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   │   ├── lib/ev2/accounts/role/role.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   ├── lib/ev2/accounts/role/permission.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   ├── lib/ev2/accounts/role/role.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   ├── lib/ev2/accounts/role/role_permission.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   │   ├── lib/ev2/accounts/role/permission.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   │   └── lib/ev2/accounts/role/role.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/ecto_types/string/trim.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/altered_document/altered_document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/constants.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/contract_type/contract_type.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/custom_field/custom_field.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   ├── lib/ev2/production/offer/offer.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   └── lib/ev2/production/project/project.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/ecto_types/string/trim.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/constants.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/custom_field/custom_field.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/document/document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/offer/offer.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/place/place.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       │   └── lib/ev2/production/project/project.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/production.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/project_default/project_default.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       │   ├── lib/ev2/ecto_types/string/trim.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       │   ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       │   └── lib/ev2/production/project/project.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       ├── lib/ev2/production/signee/signee.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       │   ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       │   ├── lib/ev2/production/document/document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       │   └── lib/ev2/production/project/project.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │       └── lib/ev2/production/vehicle/vehicle.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │           ├── lib/ev2/ecto_types/string/trim.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │           ├── lib/ev2/production/company/company.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │           │   └── lib/ev2/production/vehicle/vehicle.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │           └── lib/ev2/production/project/project.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/department/department.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/department_inclusive/department_inclusive.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/document/document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/frozen_document/frozen_document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   │   └── lib/ev2/production/offer/offer.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/job_title/job_title.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/place/place.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/prod_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   ├── lib/ev2/production/production.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   │   └── lib/ev2/production/project/project.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   ├── lib/ev2/production/project/project.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   └── lib/ev2/production/signee/signee.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   └── lib/ev2/production/offer/offer.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/offer/offer.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/company/company_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/company/company.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/contract_type/contract_type_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/contract_type/contract_type.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/contract_type/contract_type_helpers.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/prod_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/custom_field/custom_field_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/custom_field/custom_field.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/department/department_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/department/department.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/job_title/job_title.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/document/document_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/lib/doc_security/doc_security.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/lib/utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/document/document.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/production.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/document_signee/document_signee_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/document_signee/document_signee.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   ├── lib/ev2/production/document/document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   └── lib/ev2/production/signee/signee.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/frozen_document/frozen_document_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/frozen_document/frozen_document.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/job_title/job_title_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/production/job_title/job_title.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   ├── lib/ev2/production/offer/offer_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/accounts/accounts.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/accounts/role/role_api.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/accounts/user/user.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   ├── lib/ev2/engagement/engagements.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   ├── lib/ev2/engagement/eng_utils.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   ├── lib/ev2/lib/utils/time_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │   └── lib/ev2/timecards/timecards.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       ├── lib/ev2/timecards/approval/approval_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/approval/approval.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/approval/validate.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/approver/approver.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/timecard/timecard.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   └── lib/ev2/timecards/timecards.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       ├── lib/ev2/timecards/approver/approver_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/accounts/role/project_role_api.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/accounts/accounts.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/accounts/role/project_role.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/accounts/role/role_api.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/accounts/user/user.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/production/project/project.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   └── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/lib/utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/approver/approver.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/timecard/timecard.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/timecard_setting/timecard_setting.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/production/production.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/timecards/timecard/timecard.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/timecards/timecard_setting/timecard_setting_constants.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   └── lib/ev2/timecards/timecards.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   └── lib/ev2/timecards/timecards.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       ├── lib/ev2/timecards/department_inclusive/department_inclusive_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/production/department/department.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   └── lib/ev2/timecards/department_inclusive/department_inclusive.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │       ├── lib/ev2/lib/utils/changeset.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │       ├── lib/ev2/timecards/department/department.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │       │   └── lib/ev2/timecards/department_inclusive/department_inclusive.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │       ├── lib/ev2/timecards/regular_site/regular_site.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │       │   └── lib/ev2/timecards/department_inclusive/department_inclusive.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │       └── lib/ev2/timecards/timecard_setting/timecard_setting.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       ├── lib/ev2/timecards/export/export_api.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/accounts/role/role_api.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/accounts/user/user.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/engagement/engagements.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/repo.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/export/export.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   └── lib/ev2/timecards/timecard_setting/timecard_setting.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2/timecards/timecards.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   ├── lib/ev2_web/views/timecards/timecard_approval_view.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/engagement/eng_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/lib/utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/lib/utils/humanize.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   └── lib/ev2/lib/utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/lib/utils/time_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/production/prod_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/timecards/tc_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/timecards/timecard/timecard.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2/timecards/timecards.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2_web.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2_web/endpoint.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2_web/controllers/block_direct_heroku_access.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   └── lib/ev2_web/router.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2_web/router.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   ├── lib/ev2_web/views/component_view.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2/lib/utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2/lib/utils/humanize.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2/production/prod_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2/timecards/tc_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2_web.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2_web/router.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2_web/views/error_helpers.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   └── lib/ev2_web/gettext.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   ├── lib/ev2_web/views/production/document_view.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   ├── lib/ev2/production/altered_document/altered_document.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   ├── lib/ev2/production/document/document.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   ├── lib/ev2_web.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   ├── lib/ev2_web/router.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   ├── lib/ev2_web/views/component_view.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   ├── lib/ev2_web/views/error_helpers.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │   └── lib/ev2_web/views/view_helpers.ex (compile)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │       ├── lib/ev2/accounts/role/role_api.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │       ├── lib/ev2/engagement/eng_utils.ex
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │       ├── lib/ev2/engagement/engagement/engagement.ex (struct)
│   │   │   │   │   │       │   │   │   │   │       │   │   │   │       │   │   │   │       │   ├── lib/ev2/engagement/ot_setting/ot_setting.ex
...
1 Like

Am I right in thinking that this is a likely cause of an overly compile-time dependent structure:

│   │   ├── lib/ev2_web/controllers/data_plugs.ex (compile)
│   │   │   ├── lib/ev2/accounts/accounts.ex
│   │   │   │   ├── lib/ev2/accounts/core_logic.ex (compile)
│   │   │   │   ├── lib/ev2/accounts/login_attempt/login_attempt_api.ex (compile)
│   │   │   │   │   ├── lib/ev2/accounts/accounts.ex
│   │   │   │   │   ├── lib/ev2/accounts/login_attempt/login_attempt.ex (struct)
│   │   │   │   │   │   └── lib/ev2/accounts/user/user.ex
│   │   │   │   │   │       ├── lib/ev2/accounts/login_attempt/login_attempt.ex
│   │   │   │   │   │       ├── lib/ev2/accounts/role/project_role.ex
│   │   │   │   │   │       │   ├── lib/ev2/accounts/role/role.ex
│   │   │   │   │   │       │   │   └── lib/ev2/accounts/role/permission.ex

As the data plugs is reliant on a lot of other modules at runtime and is a compile time dependency of one or more other modules.

Obviously this isn’t necessarily a circular dependency, which I still don’t know if we can necessarily tell from looking at an xref graph?

Any thoughts would be appreciated.

Hi @jmurphyweb. I also have an issue where a large number of files are recompiled when I make even trial changes to many files (like adding or removing a comment). I don’t think that phrasing this as a “circular compile time dependency” though is helping, because if it were actually that then you’d have gridlock at compile time and get an actual error.

For example, these modules have a true compile time dependency on each other:

# a.ex
defmodule A do
  B.run()
end

# b.ex
defmodule B do
  A.run()
end

If I try to compile these, I get:

== Compilation error in file lib/sensetra/integration/b.ex ==
** (CompileError)  deadlocked waiting on module A
    lib/sensetra/integration/b.ex:2: (module)
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6

== Compilation error in file lib/sensetra/integration/a.ex ==
** (CompileError)  deadlocked waiting on module B
    lib/sensetra/integration/a.ex:2: (module)
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6

Compilation failed because of a deadlock between files.
The following files depended on the following modules:

  lib/sensetra/integration/b.ex => A
  lib/sensetra/integration/a.ex => B

Ensure there are no compile-time dependencies between those files and that the modules they reference exist and are correctly named
1 Like

Thanks a lot for the reply.

How are you coping!!!

Do think theres space for a tool that
a. prevents this issue from occurring in projects
b. if you have this issue, does. a clearer job of identifying the problem areas.
?

The circular dependency idea was taken from this video.

Sorry if that terminology is incorrect or misleading.

just checking that you are on latest phoenix? there was a bug in regards to plugs Do plugs always create compile time dependency? - fixed in phoenix 1.4.15…

@outlog thanks! That is my own thread actually :slight_smile: - I am not using the latest version, but my data plugs are function plugs, which this does not apply to unfortunately (only to Module plugs).

I have found the cause of my problems (eventually). This question is more about understanding if it is possible to know from analysing xref graph whether an issue exists, and if so, where.

1 Like

Just to be clarify, you aren’t suffering from a cyclical dependency issue, this is incorrect terminology. A cyclical dependency refers very specifically to a ‘cycle’ in the dependency graph. Elixir doesn’t support cyclical module dependencies and would refuse to compile in this situation (as @benwilson512 explains well here).

You seem to be concerned more about depth of your module dependency graph, rather than cycles. That’s a pretty cool idea! As far as I can tell, mix xref has access to this information (it must in parsing the module graph), but doesn’t report on it (anywhere I can find in its documentation). That would make a nifty PR or proposal.

1 Like

Really helpful, thanks a lot. I’d love to define what is possible and helpful so that I could have a crack at creating something. I think it’s unlikey for me to get something merged into XREF, but just for ease of communicating:

One suggestion:

I’d like to be able to use xref to work out which modules/files will be recompiled when I change a certain file eg: lib/my_app_web/router.ex
For the sake of explanation, let’s assume we had an additional --label available:
mix xref graph --sink lib/my_app_web/router.ex --label cause-recompile

What does it do?:

From the docs:

# To get all files that depend on lib/foo.ex at compile time
mix xref graph --label compile --sink lib/foo.ex --only-nodes

But from what I understand, this only creates a list of explicit compile-time dependancies (eg. via import, defdelegate, use

Current tool:
Meaning in my project, when I run:
mix xref graph --sink lib/my_app_web/router.ex --label compile
No files are returned.

In reality:
However, running:
echo '#' > ./lib/my_app_web/router.ex && mix compile --verbose
Recompiles 3 files:

Compiled lib/ev2_web/controllers/password_controller.ex
Compiled lib/ev2/lib/exporter/export_data_builder.ex
Compiled lib/ev2_web/router.ex

Proposal:
So running the following:
mix xref graph --sink lib/my_app_web/router.ex --label cause-recompile
Would return

lib/ev2_web/controllers/password_controller.ex
lib/ev2/lib/exporter/export_data_builder.ex
lib/ev2_web/router.ex

Why does it help?:

I think this would have helped me recently in that I could have easily analysed the current state of my actual compile time dependencies (explicit and implicit) without manually making a change and running the mix compile --verbose command.
It would also potentially be possible to quickly expose a list of modules that have many explicit & implicit compile time dependencies similar to the --format stats option

How to achieve?:

I want to know if it is possible to ascertain implicit compile time dependencies from analysing the xref graph. So far I have not been able to work it out, but maybe someone else out there can shed some light?
If that is not possible, then I assume that compiler-tracers would be the way to go.

In my examples above, the mix xref graph --sink lib/my_app_web/router.ex (without label) produces a huge list of dependencies, but here is a truncated version:

➜ mix xref graph --sink lib/my_app_web/router.ex

lib/ev2_web/views/production/custom_field_view.ex
├── lib/ev2_web/views/component_view.ex
│   ├── lib/ev2_web/views/production/document_view.ex
│   │   ├── lib/ev2_web/router.ex
│   │   │   ├── lib/ev2_web/controllers/password_controller.ex
│   │   │   │   ├── lib/ev2_web/router.ex
│   │   │   │   ├── lib/ev2_web/controllers/auth.ex
│   │   │   │   │   ├── lib/ev2_web/controllers/server_session.ex
│   │   │   │   │   │   └── lib/ev2_web/router.ex

If anyone has any thoughts on:

  1. whether this is possible by simply analysing xref graph
  2. whether this would be useful to them
  3. whether you can think of a more useful tool/api for resolving/preventing this recompilation issue
  4. any other pointers or thoughts on the above

Then I’d love to hear back